@@ -16,7 +16,12 @@ pub struct ConvertOptions {
1616 pub include_lang : Vec < String > ,
1717}
1818
19- pub fn run_unified_convert_command ( input : String , output : String , options : ConvertOptions ) {
19+ pub fn run_unified_convert_command (
20+ input : String ,
21+ output : String ,
22+ options : ConvertOptions ,
23+ strict : bool ,
24+ ) {
2025 // Special handling: when targeting xcstrings, ensure required metadata exists.
2126 // If source_language/version are missing, default to en/1.0 respectively.
2227 let wants_xcstrings = output. ends_with ( ".xcstrings" )
@@ -26,7 +31,7 @@ pub fn run_unified_convert_command(input: String, output: String, options: Conve
2631 . is_some_and ( |s| s. eq_ignore_ascii_case ( "xcstrings" ) ) ;
2732 if wants_xcstrings {
2833 println ! ( "Converting to xcstrings with default sourceLanguage if missing..." ) ;
29- match read_resources_from_any_input ( & input, options. input_format . as_ref ( ) ) . and_then (
34+ match read_resources_from_any_input ( & input, options. input_format . as_ref ( ) , strict ) . and_then (
3035 |mut resources| {
3136 // Determine source_language priority: explicit flag > metadata > default
3237 let source_language = options
@@ -110,7 +115,7 @@ pub fn run_unified_convert_command(input: String, output: String, options: Conve
110115 "Converting input to .langcodec (Resource JSON array){}..." ,
111116 filter_msg
112117 ) ;
113- match read_resources_from_any_input ( & input, options. input_format . as_ref ( ) ) . and_then (
118+ match read_resources_from_any_input ( & input, options. input_format . as_ref ( ) , strict ) . and_then (
114119 |resources| {
115120 // Apply language filtering
116121 let filtered_resources = resources
@@ -179,6 +184,46 @@ pub fn run_unified_convert_command(input: String, output: String, options: Conve
179184 }
180185 }
181186
187+ if strict {
188+ if let ( Some ( input_fmt) , Some ( output_fmt) ) = (
189+ options. input_format . as_deref ( ) ,
190+ options. output_format . as_deref ( ) ,
191+ ) {
192+ println ! ( "Strict mode: converting with explicit format hints only..." ) ;
193+ if let Err ( e) = try_explicit_format_conversion ( & input, & output, input_fmt, output_fmt) {
194+ println ! ( "❌ Strict conversion failed" ) ;
195+ eprintln ! ( "Error: {}" , e) ;
196+ std:: process:: exit ( 1 ) ;
197+ }
198+ println ! ( "✅ Successfully converted in strict mode" ) ;
199+ return ;
200+ }
201+
202+ if input. ends_with ( ".json" )
203+ || input. ends_with ( ".yaml" )
204+ || input. ends_with ( ".yml" )
205+ || input. ends_with ( ".langcodec" )
206+ {
207+ println ! ( "Strict mode: converting custom format without fallback..." ) ;
208+ if let Err ( e) = try_custom_format_conversion ( & input, & output, & options. input_format ) {
209+ println ! ( "❌ Strict conversion failed" ) ;
210+ eprintln ! ( "Error: {}" , e) ;
211+ std:: process:: exit ( 1 ) ;
212+ }
213+ println ! ( "✅ Successfully converted in strict mode" ) ;
214+ return ;
215+ }
216+
217+ println ! ( "Strict mode: converting using extension-based standard formats only..." ) ;
218+ if let Err ( e) = convert_auto ( & input, & output) {
219+ println ! ( "❌ Strict conversion failed" ) ;
220+ eprintln ! ( "Error: {}" , e) ;
221+ std:: process:: exit ( 1 ) ;
222+ }
223+ println ! ( "✅ Successfully converted in strict mode" ) ;
224+ return ;
225+ }
226+
182227 // Strategy 1: Try standard lib crate conversion first
183228 println ! ( "Trying standard format detection from file extensions..." ) ;
184229 if let Ok ( ( ) ) = convert_auto ( & input, & output) {
@@ -433,7 +478,67 @@ fn write_resources_as_langcodec(
433478pub fn read_resources_from_any_input (
434479 input : & str ,
435480 input_format_hint : Option < & String > ,
481+ strict : bool ,
436482) -> Result < Vec < langcodec:: Resource > , String > {
483+ if strict {
484+ if let Some ( fmt) = input_format_hint {
485+ let fmt_lower = fmt. to_lowercase ( ) ;
486+ let maybe_std = match fmt_lower. as_str ( ) {
487+ "strings" => Some ( langcodec:: formats:: FormatType :: Strings ( None ) ) ,
488+ "android" | "androidstrings" => {
489+ Some ( langcodec:: formats:: FormatType :: AndroidStrings ( None ) )
490+ }
491+ "xcstrings" => Some ( langcodec:: formats:: FormatType :: Xcstrings ) ,
492+ "csv" => Some ( langcodec:: formats:: FormatType :: CSV ) ,
493+ "tsv" => Some ( langcodec:: formats:: FormatType :: TSV ) ,
494+ _ => None ,
495+ } ;
496+
497+ if let Some ( std_fmt) = maybe_std {
498+ let mut codec = Codec :: new ( ) ;
499+ codec
500+ . read_file_by_type ( input, std_fmt)
501+ . map_err ( |e| format ! ( "Failed to read input with explicit format: {}" , e) ) ?;
502+ return Ok ( codec. resources ) ;
503+ }
504+
505+ let custom_format = parse_custom_format ( fmt) ?;
506+ let resources = custom_format_to_resource ( input. to_string ( ) , custom_format) ?;
507+ return Ok ( resources) ;
508+ }
509+
510+ if input. ends_with ( ".strings" )
511+ || input. ends_with ( ".xml" )
512+ || input. ends_with ( ".xcstrings" )
513+ || input. ends_with ( ".csv" )
514+ || input. ends_with ( ".tsv" )
515+ {
516+ let mut codec = Codec :: new ( ) ;
517+ codec
518+ . read_file_by_extension ( input, None )
519+ . map_err ( |e| format ! ( "Failed to read input: {}" , e) ) ?;
520+ return Ok ( codec. resources ) ;
521+ }
522+
523+ if input. ends_with ( ".json" )
524+ || input. ends_with ( ".yaml" )
525+ || input. ends_with ( ".yml" )
526+ || input. ends_with ( ".langcodec" )
527+ {
528+ validate_custom_format_file ( input) ?;
529+ let file_content = std:: fs:: read_to_string ( input)
530+ . map_err ( |e| format ! ( "Error reading file {}: {}" , input, e) ) ?;
531+ let custom_format = formats:: validate_custom_format_content ( input, & file_content) ?;
532+ let resources = custom_format_to_resource ( input. to_string ( ) , custom_format) ?;
533+ return Ok ( resources) ;
534+ }
535+
536+ return Err ( format ! (
537+ "Unsupported input format or file extension: '{}'. Supported formats: .strings, .xml, .xcstrings, .csv, .tsv, .json, .yaml, .yml, .langcodec" ,
538+ input
539+ ) ) ;
540+ }
541+
437542 // First: if explicit input format is provided and is a standard format, use it
438543 if let Some ( fmt) = input_format_hint {
439544 let fmt_lower = fmt. to_lowercase ( ) ;
0 commit comments