Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Tools/langtool/src/main.rs
3186 views
1
use std::io;
2
3
mod section;
4
use section::{line_value, Section};
5
6
mod inifile;
7
use inifile::IniFile;
8
9
use clap::Parser;
10
11
#[derive(Parser, Debug)]
12
struct Args {
13
#[command(subcommand)]
14
cmd: Command,
15
#[arg(short, long)]
16
dry_run: bool,
17
#[arg(short, long)]
18
verbose: bool,
19
}
20
21
#[derive(Parser, Debug)]
22
enum Command {
23
CopyMissingLines {
24
#[arg(short, long)]
25
dont_comment_missing: bool,
26
},
27
CommentUnknownLines {},
28
RemoveUnknownLines {},
29
AddNewKey {
30
section: String,
31
key: String,
32
},
33
AddNewKeyValue {
34
section: String,
35
key: String,
36
value: String,
37
},
38
MoveKey {
39
old: String,
40
new: String,
41
key: String,
42
},
43
CopyKey {
44
old: String,
45
new: String,
46
key: String,
47
},
48
DupeKey {
49
section: String,
50
old: String,
51
new: String,
52
},
53
RenameKey {
54
section: String,
55
old: String,
56
new: String,
57
},
58
SortSection {
59
section: String,
60
},
61
RemoveKey {
62
section: String,
63
key: String,
64
},
65
GetNewKeys,
66
ImportSingle {
67
filename: String,
68
section: String,
69
key: String,
70
},
71
}
72
73
fn copy_missing_lines(
74
reference_ini: &IniFile,
75
target_ini: &mut IniFile,
76
comment_missing: bool,
77
) -> io::Result<()> {
78
for reference_section in &reference_ini.sections {
79
// Insert any missing full sections.
80
if !target_ini.insert_section_if_missing(reference_section) {
81
if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) {
82
for line in &reference_section.lines {
83
target_section.insert_line_if_missing(line);
84
}
85
86
//target_section.remove_lines_if_not_in(reference_section);
87
if comment_missing {
88
target_section.comment_out_lines_if_not_in(reference_section);
89
}
90
}
91
} else {
92
// Note: insert_section_if_missing will copy the entire section,
93
// no need to loop over the lines here.
94
println!("Inserted missing section: {}", reference_section.name);
95
}
96
}
97
Ok(())
98
}
99
100
fn deal_with_unknown_lines(
101
reference_ini: &IniFile,
102
target_ini: &mut IniFile,
103
remove: bool,
104
) -> io::Result<()> {
105
for reference_section in &reference_ini.sections {
106
if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) {
107
if remove {
108
target_section.remove_lines_if_not_in(reference_section);
109
} else {
110
target_section.comment_out_lines_if_not_in(reference_section);
111
}
112
}
113
}
114
Ok(())
115
}
116
117
fn print_keys_if_not_in(
118
reference_ini: &IniFile,
119
target_ini: &mut IniFile,
120
header: &str,
121
) -> io::Result<()> {
122
for reference_section in &reference_ini.sections {
123
if let Some(target_section) = target_ini.get_section_mut(&reference_section.name) {
124
let keys = target_section.get_keys_if_not_in(reference_section);
125
if !keys.is_empty() {
126
println!("{} ({})", reference_section.name, header);
127
for key in &keys {
128
println!("- {key}");
129
}
130
}
131
}
132
}
133
Ok(())
134
}
135
136
fn move_key(target_ini: &mut IniFile, old: &str, new: &str, key: &str) -> io::Result<()> {
137
if let Some(old_section) = target_ini.get_section_mut(old) {
138
if let Some(line) = old_section.remove_line(key) {
139
if let Some(new_section) = target_ini.get_section_mut(new) {
140
new_section.insert_line_if_missing(&line);
141
} else {
142
println!("No new section {new}");
143
}
144
} else {
145
println!("No key {key} in section {old}");
146
}
147
} else {
148
println!("No old section {old}");
149
}
150
Ok(())
151
}
152
153
fn copy_key(target_ini: &mut IniFile, old: &str, new: &str, key: &str) -> io::Result<()> {
154
if let Some(old_section) = target_ini.get_section_mut(old) {
155
if let Some(line) = old_section.get_line(key) {
156
if let Some(new_section) = target_ini.get_section_mut(new) {
157
new_section.insert_line_if_missing(&line);
158
} else {
159
println!("No new section {new}");
160
}
161
} else {
162
println!("No key {key} in section {old}");
163
}
164
} else {
165
println!("No old section {old}");
166
}
167
Ok(())
168
}
169
170
fn remove_key(target_ini: &mut IniFile, section: &str, key: &str) -> io::Result<()> {
171
if let Some(old_section) = target_ini.get_section_mut(section) {
172
old_section.remove_line(key);
173
} else {
174
println!("No section {section}");
175
}
176
Ok(())
177
}
178
179
fn add_new_key(target_ini: &mut IniFile, section: &str, key: &str, value: &str) -> io::Result<()> {
180
if let Some(section) = target_ini.get_section_mut(section) {
181
section.insert_line_if_missing(&format!("{key} = {value}"));
182
} else {
183
println!("No section {section}");
184
}
185
Ok(())
186
}
187
188
fn rename_key(target_ini: &mut IniFile, section: &str, old: &str, new: &str) -> io::Result<()> {
189
if let Some(section) = target_ini.get_section_mut(section) {
190
section.rename_key(old, new);
191
} else {
192
println!("No section {section}");
193
}
194
Ok(())
195
}
196
197
fn dupe_key(target_ini: &mut IniFile, section: &str, old: &str, new: &str) -> io::Result<()> {
198
if let Some(section) = target_ini.get_section_mut(section) {
199
section.dupe_key(old, new);
200
} else {
201
println!("No section {section}");
202
}
203
Ok(())
204
}
205
206
fn sort_section(target_ini: &mut IniFile, section: &str) -> io::Result<()> {
207
if let Some(section) = target_ini.get_section_mut(section) {
208
section.sort();
209
} else {
210
println!("No section {section}");
211
}
212
Ok(())
213
}
214
215
// TODO: Look into using https://github.com/Byron/google-apis-rs/tree/main/gen/translate2 for initial translations.
216
217
fn main() {
218
let opt = Args::parse();
219
220
// TODO: Grab extra arguments from opt somehow.
221
let args: Vec<String> = vec![]; //std::env::args().skip(1).collect();
222
let mut filenames = args;
223
224
let root = "../../assets/lang";
225
let reference_ini_filename = "en_US.ini";
226
227
let mut reference_ini =
228
IniFile::parse(&format!("{root}/{reference_ini_filename}")).unwrap();
229
230
if filenames.is_empty() {
231
// Grab them all.
232
for path in std::fs::read_dir(root).unwrap() {
233
let path = path.unwrap();
234
if path.file_name() == reference_ini_filename {
235
continue;
236
}
237
let filename = path.file_name();
238
let filename = filename.to_string_lossy();
239
if !filename.ends_with(".ini") {
240
continue;
241
}
242
filenames.push(path.file_name().to_string_lossy().to_string());
243
}
244
}
245
246
let mut single_ini_section: Option<Section> = None;
247
if let Command::ImportSingle {
248
filename,
249
section,
250
key: _,
251
} = &opt.cmd
252
{
253
if let Ok(single_ini) = IniFile::parse(filename) {
254
if let Some(single_section) = single_ini.get_section("Single") {
255
single_ini_section = Some(single_section.clone());
256
} else {
257
println!("No section {section} in {filename}");
258
}
259
} else {
260
println!("Failed to parse {filename}");
261
return;
262
}
263
}
264
265
for filename in filenames {
266
let reference_ini = &reference_ini;
267
if filename == "langtool" {
268
// Get this from cargo run for some reason.
269
continue;
270
}
271
let target_ini_filename = format!("{root}/{filename}");
272
if opt.verbose {
273
println!("Langtool processing {target_ini_filename}");
274
}
275
276
let mut target_ini = IniFile::parse(&target_ini_filename).unwrap();
277
278
match opt.cmd {
279
Command::CopyMissingLines {
280
dont_comment_missing,
281
} => {
282
copy_missing_lines(reference_ini, &mut target_ini, !dont_comment_missing).unwrap();
283
}
284
Command::CommentUnknownLines {} => {
285
deal_with_unknown_lines(reference_ini, &mut target_ini, false).unwrap();
286
}
287
Command::RemoveUnknownLines {} => {
288
deal_with_unknown_lines(reference_ini, &mut target_ini, true).unwrap();
289
}
290
Command::GetNewKeys => {
291
print_keys_if_not_in(reference_ini, &mut target_ini, &target_ini_filename).unwrap();
292
}
293
Command::SortSection { ref section } => sort_section(&mut target_ini, section).unwrap(),
294
Command::RenameKey {
295
ref section,
296
ref old,
297
ref new,
298
} => rename_key(&mut target_ini, section, old, new).unwrap(),
299
Command::AddNewKey {
300
ref section,
301
ref key,
302
} => add_new_key(&mut target_ini, section, key, key).unwrap(),
303
Command::AddNewKeyValue {
304
ref section,
305
ref key,
306
ref value,
307
} => add_new_key(&mut target_ini, section, key, value).unwrap(),
308
Command::MoveKey {
309
ref old,
310
ref new,
311
ref key,
312
} => {
313
move_key(&mut target_ini, old, new, key).unwrap();
314
}
315
Command::CopyKey {
316
// Copies between sections
317
ref old,
318
ref new,
319
ref key,
320
} => {
321
copy_key(&mut target_ini, old, new, key).unwrap();
322
}
323
Command::DupeKey {
324
ref section,
325
ref old,
326
ref new,
327
} => {
328
dupe_key(&mut target_ini, section, old, new).unwrap();
329
}
330
Command::RemoveKey {
331
ref section,
332
ref key,
333
} => {
334
remove_key(&mut target_ini, section, key).unwrap();
335
}
336
Command::ImportSingle {
337
filename: _,
338
ref section,
339
ref key,
340
} => {
341
let lang_id = filename.strip_suffix(".ini").unwrap();
342
if let Some(single_section) = &single_ini_section {
343
if let Some(target_section) = target_ini.get_section_mut(section) {
344
if let Some(single_line) = single_section.get_line(lang_id) {
345
if let Some(value) = line_value(&single_line) {
346
println!(
347
"Inserting value {value} for key {key} in section {section} in {target_ini_filename}"
348
);
349
if !target_section
350
.insert_line_if_missing(&format!("{key} = {value}"))
351
{
352
// Didn't insert it, so it exists. We need to replace it.
353
target_section.set_value(key, value);
354
}
355
}
356
} else {
357
println!("No lang_id {lang_id} in single section");
358
}
359
} else {
360
println!("No section {section} in {target_ini_filename}");
361
}
362
} else {
363
println!("No section {section} in {filename}");
364
}
365
}
366
}
367
368
if !opt.dry_run {
369
target_ini.write().unwrap();
370
}
371
}
372
373
println!("Langtool processing {reference_ini_filename}");
374
375
// Some commands also apply to the reference ini.
376
match opt.cmd {
377
Command::AddNewKey {
378
ref section,
379
ref key,
380
} => {
381
add_new_key(&mut reference_ini, section, key, key).unwrap();
382
}
383
Command::AddNewKeyValue {
384
ref section,
385
ref key,
386
ref value,
387
} => {
388
add_new_key(&mut reference_ini, section, key, value).unwrap();
389
}
390
Command::SortSection { ref section } => sort_section(&mut reference_ini, section).unwrap(),
391
Command::RenameKey {
392
ref section,
393
ref old,
394
ref new,
395
} => {
396
if old == new {
397
println!("WARNING: old == new");
398
}
399
rename_key(&mut reference_ini, section, old, new).unwrap();
400
}
401
Command::MoveKey {
402
ref old,
403
ref new,
404
ref key,
405
} => {
406
move_key(&mut reference_ini, old, new, key).unwrap();
407
}
408
Command::CopyKey {
409
// between sections
410
ref old,
411
ref new,
412
ref key,
413
} => {
414
copy_key(&mut reference_ini, old, new, key).unwrap();
415
}
416
Command::DupeKey {
417
// Inside a section, preserving a value
418
ref section,
419
ref old,
420
ref new,
421
} => {
422
dupe_key(&mut reference_ini, section, old, new).unwrap();
423
}
424
Command::RemoveKey {
425
ref section,
426
ref key,
427
} => {
428
remove_key(&mut reference_ini, section, key).unwrap();
429
}
430
_ => {}
431
}
432
433
if !opt.dry_run {
434
reference_ini.write().unwrap();
435
}
436
}
437
438