11use crate :: convert:: read_resources_from_any_input;
22use crate :: validation:: { validate_file_path, validate_language_code, validate_output_path} ;
3- use langcodec:: { Resource , Translation } ;
4- use serde_json:: json;
5- use std:: collections:: { BTreeMap , BTreeSet } ;
3+ use langcodec:: { DiffOptions as LibDiffOptions , DiffReport , Translation , diff_resources} ;
64
75#[ derive( Debug , Clone ) ]
86pub struct DiffOptions {
@@ -14,45 +12,6 @@ pub struct DiffOptions {
1412 pub strict : bool ,
1513}
1614
17- #[ derive( Debug , Clone ) ]
18- struct ChangedItem {
19- key : String ,
20- source : Translation ,
21- target : Translation ,
22- }
23-
24- #[ derive( Debug , Clone , Default ) ]
25- struct LanguageDiff {
26- language : String ,
27- added : Vec < String > ,
28- removed : Vec < String > ,
29- changed : Vec < ChangedItem > ,
30- unchanged : usize ,
31- }
32-
33- #[ derive( Debug , Clone , Default ) ]
34- struct DiffSummary {
35- languages : usize ,
36- added : usize ,
37- removed : usize ,
38- changed : usize ,
39- unchanged : usize ,
40- }
41-
42- fn normalize_lang ( lang : & str ) -> String {
43- lang. trim ( ) . replace ( '_' , "-" ) . to_ascii_lowercase ( )
44- }
45-
46- fn lang_base ( lang : & str ) -> & str {
47- lang. split ( '-' ) . next ( ) . unwrap_or ( lang)
48- }
49-
50- fn lang_matches ( resource_lang : & str , requested_lang : & str ) -> bool {
51- let res = normalize_lang ( resource_lang) ;
52- let req = normalize_lang ( requested_lang) ;
53- res == req || lang_base ( & res) == lang_base ( & req)
54- }
55-
5615fn translation_as_text ( value : & Translation ) -> String {
5716 match value {
5817 Translation :: Empty => String :: new ( ) ,
@@ -67,79 +26,6 @@ fn translation_as_text(value: &Translation) -> String {
6726 }
6827}
6928
70- fn build_language_map ( resources : Vec < Resource > ) -> BTreeMap < String , BTreeMap < String , Translation > > {
71- let mut map: BTreeMap < String , BTreeMap < String , Translation > > = BTreeMap :: new ( ) ;
72- for resource in resources {
73- let lang = normalize_lang ( & resource. metadata . language ) ;
74- let entry_map = map. entry ( lang) . or_default ( ) ;
75- for entry in resource. entries {
76- entry_map. insert ( entry. id , entry. value ) ;
77- }
78- }
79- map
80- }
81-
82- fn collect_diff (
83- source : & BTreeMap < String , BTreeMap < String , Translation > > ,
84- target : & BTreeMap < String , BTreeMap < String , Translation > > ,
85- lang_filter : & Option < String > ,
86- ) -> ( DiffSummary , Vec < LanguageDiff > ) {
87- let mut summary = DiffSummary :: default ( ) ;
88- let mut languages = BTreeSet :: new ( ) ;
89- languages. extend ( source. keys ( ) . cloned ( ) ) ;
90- languages. extend ( target. keys ( ) . cloned ( ) ) ;
91-
92- let mut per_language = Vec :: new ( ) ;
93- for lang in languages {
94- if let Some ( filter) = lang_filter
95- && !lang_matches ( & lang, filter)
96- {
97- continue ;
98- }
99-
100- let source_entries = source. get ( & lang) ;
101- let target_entries = target. get ( & lang) ;
102-
103- let mut all_keys = BTreeSet :: new ( ) ;
104- if let Some ( entries) = source_entries {
105- all_keys. extend ( entries. keys ( ) . cloned ( ) ) ;
106- }
107- if let Some ( entries) = target_entries {
108- all_keys. extend ( entries. keys ( ) . cloned ( ) ) ;
109- }
110-
111- let mut diff = LanguageDiff {
112- language : lang. clone ( ) ,
113- ..LanguageDiff :: default ( )
114- } ;
115-
116- for key in all_keys {
117- let source_value = source_entries. and_then ( |m| m. get ( & key) ) ;
118- let target_value = target_entries. and_then ( |m| m. get ( & key) ) ;
119- match ( source_value, target_value) {
120- ( Some ( _) , None ) => diff. added . push ( key) ,
121- ( None , Some ( _) ) => diff. removed . push ( key) ,
122- ( Some ( s) , Some ( t) ) if s != t => diff. changed . push ( ChangedItem {
123- key,
124- source : s. clone ( ) ,
125- target : t. clone ( ) ,
126- } ) ,
127- ( Some ( _) , Some ( _) ) => diff. unchanged += 1 ,
128- ( None , None ) => { }
129- }
130- }
131-
132- summary. languages += 1 ;
133- summary. added += diff. added . len ( ) ;
134- summary. removed += diff. removed . len ( ) ;
135- summary. changed += diff. changed . len ( ) ;
136- summary. unchanged += diff. unchanged ;
137- per_language. push ( diff) ;
138- }
139-
140- ( summary, per_language)
141- }
142-
14329fn print_or_write ( output : Option < & String > , content : & str ) -> Result < ( ) , String > {
14430 if let Some ( path) = output {
14531 std:: fs:: write ( path, content) . map_err ( |e| format ! ( "Failed to write {}: {}" , path, e) ) ?;
@@ -150,16 +36,19 @@ fn print_or_write(output: Option<&String>, content: &str) -> Result<(), String>
15036 Ok ( ( ) )
15137}
15238
153- fn render_human ( summary : & DiffSummary , per_language : & [ LanguageDiff ] ) -> String {
39+ fn render_human ( report : & DiffReport ) -> String {
15440 let mut lines = Vec :: new ( ) ;
15541 lines. push ( "=== Diff ===" . to_string ( ) ) ;
156- lines. push ( format ! ( "Languages: {}" , summary. languages) ) ;
42+ lines. push ( format ! ( "Languages: {}" , report . summary. languages) ) ;
15743 lines. push ( format ! (
15844 "Totals: added={}, removed={}, changed={}, unchanged={}" ,
159- summary. added, summary. removed, summary. changed, summary. unchanged
45+ report. summary. added,
46+ report. summary. removed,
47+ report. summary. changed,
48+ report. summary. unchanged
16049 ) ) ;
16150
162- for lang in per_language {
51+ for lang in & report . languages {
16352 lines. push ( format ! ( "\n Language: {}" , lang. language) ) ;
16453 lines. push ( format ! ( " added: {}" , lang. added. len( ) ) ) ;
16554 lines. push ( format ! ( " removed: {}" , lang. removed. len( ) ) ) ;
@@ -188,23 +77,24 @@ fn render_human(summary: &DiffSummary, per_language: &[LanguageDiff]) -> String
18877 lines. join ( "\n " )
18978}
19079
191- fn render_json ( summary : & DiffSummary , per_language : & [ LanguageDiff ] ) -> Result < String , String > {
192- let languages_json: Vec < _ > = per_language
80+ fn render_json ( report : & DiffReport ) -> Result < String , String > {
81+ let languages_json: Vec < _ > = report
82+ . languages
19383 . iter ( )
19484 . map ( |lang| {
19585 let changed: Vec < _ > = lang
19686 . changed
19787 . iter ( )
19888 . map ( |item| {
199- json ! ( {
89+ serde_json :: json!( {
20090 "key" : item. key,
20191 "source" : item. source,
20292 "target" : item. target,
20393 } )
20494 } )
20595 . collect ( ) ;
20696
207- json ! ( {
97+ serde_json :: json!( {
20898 "language" : lang. language,
20999 "counts" : {
210100 "added" : lang. added. len( ) ,
@@ -219,18 +109,18 @@ fn render_json(summary: &DiffSummary, per_language: &[LanguageDiff]) -> Result<S
219109 } )
220110 . collect ( ) ;
221111
222- let report = json ! ( {
112+ let payload = serde_json :: json!( {
223113 "summary" : {
224- "languages" : summary. languages,
225- "added" : summary. added,
226- "removed" : summary. removed,
227- "changed" : summary. changed,
228- "unchanged" : summary. unchanged,
114+ "languages" : report . summary. languages,
115+ "added" : report . summary. added,
116+ "removed" : report . summary. removed,
117+ "changed" : report . summary. changed,
118+ "unchanged" : report . summary. unchanged,
229119 } ,
230120 "languages" : languages_json,
231121 } ) ;
232122
233- serde_json:: to_string_pretty ( & report )
123+ serde_json:: to_string_pretty ( & payload )
234124 . map_err ( |e| format ! ( "Failed to serialize diff report JSON: {}" , e) )
235125}
236126
@@ -247,15 +137,19 @@ pub fn run_diff_command(opts: DiffOptions) -> Result<(), String> {
247137 let source_resources = read_resources_from_any_input ( & opts. source , None , opts. strict ) ?;
248138 let target_resources = read_resources_from_any_input ( & opts. target , None , opts. strict ) ?;
249139
250- let source_map = build_language_map ( source_resources) ;
251- let target_map = build_language_map ( target_resources) ;
252- let ( summary, per_language) = collect_diff ( & source_map, & target_map, & opts. lang ) ;
140+ let report = diff_resources (
141+ & source_resources,
142+ & target_resources,
143+ & LibDiffOptions {
144+ language_filter : opts. lang . clone ( ) ,
145+ } ,
146+ ) ;
253147
254148 if opts. json {
255- let rendered = render_json ( & summary , & per_language ) ?;
149+ let rendered = render_json ( & report ) ?;
256150 print_or_write ( opts. output . as_ref ( ) , & rendered) ?;
257151 } else {
258- let rendered = render_human ( & summary , & per_language ) ;
152+ let rendered = render_human ( & report ) ;
259153 print_or_write ( opts. output . as_ref ( ) , & rendered) ?;
260154 }
261155
0 commit comments