@@ -1039,30 +1039,78 @@ private static T CheckedCast<T>(object input, string errorMessage, bool failOnEr
10391039 return new ValueExpression ( date . ToString ( CultureInfo . InvariantCulture ) , date . ToString ( CultureInfo . InvariantCulture ) ) ;
10401040 } ;
10411041
1042+ /// <summary>
1043+ /// Formats a single value with a format specifier (like .ToString(format))
1044+ /// </summary>
10421045 public static FunctionHandler Format = ( primary , service , tracing , interpreterConfig , parameters ) =>
10431046 {
10441047 if ( parameters . Count < 2 )
10451048 {
1046- throw new InvalidPluginExecutionException ( "Format needs a value to format and a config for defining further options " ) ;
1049+ throw new InvalidPluginExecutionException ( "Format needs a value and a format string " ) ;
10471050 }
10481051
10491052 var value = parameters [ 0 ] . Value ;
1053+
1054+ // Handle special CRM types
1055+ if ( value is Money money ) value = money . Value ;
1056+ if ( value is OptionSetValue optionSet ) value = optionSet . Value ;
1057+
1058+ // Support both styles:
1059+ // Format(value, { format: "C2" })
1060+ // Format(value, { format: "{0:C2}" })
10501061 var config = GetConfig ( parameters ) ;
1051- var format = config . GetValue < string > ( "format" , "format must be a string!" ) ;
1062+ var formatString = config . GetValue < string > ( "format" , "format must be a string!" ) ;
1063+
1064+ try
1065+ {
1066+ string formatted ;
1067+
1068+ // If format contains {0:, use string.Format
1069+ if ( formatString . Contains ( "{0:" ) || formatString . Contains ( "{0}" ) )
1070+ {
1071+ formatted = string . Format ( CultureInfo . InvariantCulture , formatString , value ) ;
1072+ }
1073+ // Otherwise, use direct formatting
1074+ else if ( value is IFormattable formattable )
1075+ {
1076+ formatted = formattable . ToString ( formatString , CultureInfo . InvariantCulture ) ;
1077+ }
1078+ else
1079+ {
1080+ formatted = value ? . ToString ( ) ?? string . Empty ;
1081+ }
1082+
1083+ return new ValueExpression ( formatted , formatted ) ;
1084+ }
1085+ catch ( FormatException ex )
1086+ {
1087+ throw new InvalidPluginExecutionException ( $ "Invalid format '{ formatString } ': { ex . Message } ") ;
1088+ }
1089+ } ;
10521090
1053- var knownTypes = new Dictionary < Type , Func < object , ValueExpression > >
1091+ /// <summary>
1092+ /// Composes multiple values into a format string (like string.Format)
1093+ /// </summary>
1094+ public static FunctionHandler FormatString = ( primary , service , tracing , interpreterConfig , parameters ) =>
1095+ {
1096+ if ( parameters . Count < 1 )
10541097 {
1055- { typeof ( Money ) , ( obj ) => { var val = obj as Money ; var formatted = string . Format ( CultureInfo . InvariantCulture , format , val . Value ) ; return new ValueExpression ( formatted , formatted ) ; } }
1056- } ;
1098+ throw new InvalidPluginExecutionException ( "FormatString needs at least a format string" ) ;
1099+ }
10571100
1058- if ( knownTypes . ContainsKey ( value . GetType ( ) ) )
1101+ var format = CheckedCast < string > ( parameters [ 0 ] . Value , "First parameter must be a format string" ) ;
1102+
1103+ // Positional parameters only - exactly like C# string.Format
1104+ var args = parameters . Skip ( 1 ) . Select ( p => p . Value ) . ToArray ( ) ;
1105+
1106+ try
10591107 {
1060- return knownTypes [ value . GetType ( ) ] ( value ) ;
1108+ var result = string . Format ( CultureInfo . InvariantCulture , format , args ) ;
1109+ return new ValueExpression ( result , result ) ;
10611110 }
1062- else
1111+ catch ( FormatException ex )
10631112 {
1064- var formatted = string . Format ( CultureInfo . InvariantCulture , format , value ) ;
1065- return new ValueExpression ( formatted , formatted ) ;
1113+ throw new InvalidPluginExecutionException ( $ "Invalid format string: { ex . Message } ") ;
10661114 }
10671115 } ;
10681116
0 commit comments