11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- use crate :: args:: { ConfigSubCommand , DscType , ExtensionSubCommand , GetOutputFormat , ListOutputFormat , OutputFormat , ResourceSubCommand } ;
4+ use crate :: args:: { ConfigSubCommand , SchemaType , ExtensionSubCommand , FunctionSubCommand , GetOutputFormat , ListOutputFormat , OutputFormat , ResourceSubCommand } ;
55use crate :: resolve:: { get_contents, Include } ;
66use crate :: resource_command:: { get_resource, self } ;
77use crate :: tablewriter:: Table ;
88use crate :: util:: { get_input, get_schema, in_desired_state, set_dscconfigroot, validate_json, write_object, DSC_CONFIG_ROOT , EXIT_DSC_ASSERTION_FAILED , EXIT_DSC_ERROR , EXIT_INVALID_ARGS , EXIT_INVALID_INPUT , EXIT_JSON_ERROR } ;
9+ use dsc_lib:: functions:: AcceptedArgKind ;
910use dsc_lib:: {
1011 configure:: {
1112 config_doc:: {
@@ -28,8 +29,11 @@ use dsc_lib::{
2829 dscresources:: dscresource:: { Capability , ImplementedAs , Invoke } ,
2930 dscresources:: resource_manifest:: { import_manifest, ResourceManifest } ,
3031 extensions:: dscextension:: Capability as ExtensionCapability ,
32+ functions:: FunctionDispatcher ,
3133 progress:: ProgressFormat ,
34+ util:: convert_wildcard_to_regex,
3235} ;
36+ use regex:: RegexBuilder ;
3337use rust_i18n:: t;
3438use std:: {
3539 collections:: HashMap ,
@@ -473,7 +477,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, parame
473477pub fn validate_config ( config : & Configuration , progress_format : ProgressFormat ) -> Result < ( ) , DscError > {
474478 // first validate against the config schema
475479 debug ! ( "{}" , t!( "subcommand.validatingConfiguration" ) ) ;
476- let schema = serde_json:: to_value ( get_schema ( DscType :: Configuration ) ) ?;
480+ let schema = serde_json:: to_value ( get_schema ( SchemaType :: Configuration ) ) ?;
477481 let config_value = serde_json:: to_value ( config) ?;
478482 validate_json ( "Configuration" , & schema, & config_value) ?;
479483 let mut dsc = DscManager :: new ( ) ?;
@@ -562,6 +566,15 @@ pub fn extension(subcommand: &ExtensionSubCommand, progress_format: ProgressForm
562566 }
563567}
564568
569+ pub fn function ( subcommand : & FunctionSubCommand ) {
570+ let functions = FunctionDispatcher :: new ( ) ;
571+ match subcommand {
572+ FunctionSubCommand :: List { function_name, output_format } => {
573+ list_functions ( & functions, function_name. as_ref ( ) , output_format. as_ref ( ) ) ;
574+ } ,
575+ }
576+ }
577+
565578#[ allow( clippy:: too_many_lines) ]
566579pub fn resource ( subcommand : & ResourceSubCommand , progress_format : ProgressFormat ) {
567580 let mut dsc = match DscManager :: new ( ) {
@@ -632,10 +645,10 @@ fn list_extensions(dsc: &mut DscManager, extension_name: Option<&String>, format
632645 let mut include_separator = false ;
633646 for manifest_resource in dsc. list_available ( & DiscoveryKind :: Extension , extension_name. unwrap_or ( & String :: from ( "*" ) ) , "" , progress_format) {
634647 if let ImportedManifest :: Extension ( extension) = manifest_resource {
635- let mut capabilities = "-" . to_string ( ) ;
636648 let capability_types = [
637649 ( ExtensionCapability :: Discover , "d" ) ,
638650 ] ;
651+ let mut capabilities = "-" . repeat ( capability_types. len ( ) ) ;
639652
640653 for ( i, ( capability, letter) ) in capability_types. iter ( ) . enumerate ( ) {
641654 if extension. capabilities . contains ( capability) {
@@ -680,6 +693,97 @@ fn list_extensions(dsc: &mut DscManager, extension_name: Option<&String>, format
680693 }
681694}
682695
696+ fn list_functions ( functions : & FunctionDispatcher , function_name : Option < & String > , output_format : Option < & ListOutputFormat > ) {
697+ let mut write_table = false ;
698+ let mut table = Table :: new ( & [
699+ t ! ( "subcommand.tableHeader_functionCategory" ) . to_string ( ) . as_ref ( ) ,
700+ t ! ( "subcommand.tableHeader_functionName" ) . to_string ( ) . as_ref ( ) ,
701+ t ! ( "subcommand.tableHeader_minArgs" ) . to_string ( ) . as_ref ( ) ,
702+ t ! ( "subcommand.tableHeader_maxArgs" ) . to_string ( ) . as_ref ( ) ,
703+ t ! ( "subcommand.tableHeader_argTypes" ) . to_string ( ) . as_ref ( ) ,
704+ t ! ( "subcommand.tableHeader_description" ) . to_string ( ) . as_ref ( ) ,
705+ ] ) ;
706+ if output_format. is_none ( ) && io:: stdout ( ) . is_terminal ( ) {
707+ // write as table if format is not specified and interactive
708+ write_table = true ;
709+ }
710+ let mut include_separator = false ;
711+ let accepted_arg_types= [
712+ ( AcceptedArgKind :: Array , "a" ) ,
713+ ( AcceptedArgKind :: Boolean , "b" ) ,
714+ ( AcceptedArgKind :: Number , "n" ) ,
715+ ( AcceptedArgKind :: String , "s" ) ,
716+ ( AcceptedArgKind :: Object , "o" ) ,
717+ ] ;
718+
719+ let asterisks = String :: from ( "*" ) ;
720+ let name = function_name. unwrap_or ( & asterisks) ;
721+ let regex_str = convert_wildcard_to_regex ( name) ;
722+ let mut regex_builder = RegexBuilder :: new ( & regex_str) ;
723+ regex_builder. case_insensitive ( true ) ;
724+ let Ok ( regex) = regex_builder. build ( ) else {
725+ error ! ( "{}: {}" , t!( "subcommand.invalidFunctionFilter" ) , regex_str) ;
726+ exit ( EXIT_INVALID_ARGS ) ;
727+ } ;
728+
729+ let mut functions_list = functions. list ( ) ;
730+ functions_list. sort ( ) ;
731+ for function in functions_list {
732+ if !regex. is_match ( & function. name ) {
733+ continue ;
734+ }
735+
736+ if write_table {
737+ // construct arg_types from '-' times number of accepted_arg_types
738+ let mut arg_types = "-" . repeat ( accepted_arg_types. len ( ) ) ;
739+ for ( i, ( arg_type, letter) ) in accepted_arg_types. iter ( ) . enumerate ( ) {
740+ if function. accepted_arg_types . contains ( arg_type) {
741+ arg_types. replace_range ( i..=i, letter) ;
742+ }
743+ }
744+
745+ let max_args = if function. max_args == usize:: MAX {
746+ t ! ( "subcommand.maxInt" ) . to_string ( )
747+ } else {
748+ function. max_args . to_string ( )
749+ } ;
750+
751+ table. add_row ( vec ! [
752+ function. category. to_string( ) ,
753+ function. name,
754+ function. min_args. to_string( ) ,
755+ max_args,
756+ arg_types,
757+ function. description
758+ ] ) ;
759+ }
760+ else {
761+ let json = match serde_json:: to_string ( & function) {
762+ Ok ( json) => json,
763+ Err ( err) => {
764+ error ! ( "JSON: {err}" ) ;
765+ exit ( EXIT_JSON_ERROR ) ;
766+ }
767+ } ;
768+ let format = match output_format {
769+ Some ( ListOutputFormat :: Json ) => Some ( OutputFormat :: Json ) ,
770+ Some ( ListOutputFormat :: PrettyJson ) => Some ( OutputFormat :: PrettyJson ) ,
771+ Some ( ListOutputFormat :: Yaml ) => Some ( OutputFormat :: Yaml ) ,
772+ _ => None ,
773+ } ;
774+ write_object ( & json, format. as_ref ( ) , include_separator) ;
775+ include_separator = true ;
776+ // insert newline separating instances if writing to console
777+ if io:: stdout ( ) . is_terminal ( ) { println ! ( ) ; }
778+ }
779+ }
780+
781+ if write_table {
782+ let truncate = output_format != Some ( & ListOutputFormat :: TableNoTruncate ) ;
783+ table. print ( truncate) ;
784+ }
785+ }
786+
683787fn list_resources ( dsc : & mut DscManager , resource_name : Option < & String > , adapter_name : Option < & String > , description : Option < & String > , tags : Option < & Vec < String > > , format : Option < & ListOutputFormat > , progress_format : ProgressFormat ) {
684788 let mut write_table = false ;
685789 let mut table = Table :: new ( & [
@@ -697,7 +801,6 @@ fn list_resources(dsc: &mut DscManager, resource_name: Option<&String>, adapter_
697801 let mut include_separator = false ;
698802 for manifest_resource in dsc. list_available ( & DiscoveryKind :: Resource , resource_name. unwrap_or ( & String :: from ( "*" ) ) , adapter_name. unwrap_or ( & String :: new ( ) ) , progress_format) {
699803 if let ImportedManifest :: Resource ( resource) = manifest_resource {
700- let mut capabilities = "--------" . to_string ( ) ;
701804 let capability_types = [
702805 ( Capability :: Get , "g" ) ,
703806 ( Capability :: Set , "s" ) ,
@@ -708,6 +811,7 @@ fn list_resources(dsc: &mut DscManager, resource_name: Option<&String>, adapter_
708811 ( Capability :: Export , "e" ) ,
709812 ( Capability :: Resolve , "r" ) ,
710813 ] ;
814+ let mut capabilities = "-" . repeat ( capability_types. len ( ) ) ;
711815
712816 for ( i, ( capability, letter) ) in capability_types. iter ( ) . enumerate ( ) {
713817 if resource. capabilities . contains ( capability) {
0 commit comments