Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Tools/langtool/src/section.rs
3186 views
1
// Super simplified ini file processor.
2
// Doesn't even bother with understanding comments.
3
// Just understands section headings and
4
// keys and values, split by ' = '.
5
6
#[derive(Debug, Clone)]
7
pub struct Section {
8
pub name: String,
9
pub title_line: String,
10
pub lines: Vec<String>,
11
}
12
13
pub fn line_value(line: &str) -> Option<&str> {
14
let line = line.trim();
15
if let Some(pos) = line.find(" =") {
16
let value = &line[pos + 2..];
17
if value.is_empty() {
18
return None;
19
}
20
return Some(value.trim());
21
}
22
None
23
}
24
25
impl Section {
26
pub fn remove_line(&mut self, key: &str) -> Option<String> {
27
let mut remove_index = None;
28
for (index, line) in self.lines.iter().enumerate() {
29
let prefix = if let Some(pos) = line.find(" =") {
30
&line[0..pos]
31
} else {
32
continue;
33
};
34
35
if prefix.eq_ignore_ascii_case(key) {
36
remove_index = Some(index);
37
break;
38
}
39
}
40
41
if let Some(remove_index) = remove_index {
42
Some(self.lines.remove(remove_index))
43
} else {
44
None
45
}
46
}
47
48
pub fn get_line(&self, key: &str) -> Option<String> {
49
for line in self.lines.iter() {
50
let prefix = if let Some(pos) = line.find(" =") {
51
&line[0..pos]
52
} else {
53
continue;
54
};
55
56
if prefix.eq_ignore_ascii_case(key) {
57
return Some(line.clone());
58
}
59
}
60
None
61
}
62
63
pub fn insert_line_if_missing(&mut self, line: &str) -> bool {
64
let prefix = if let Some(pos) = line.find(" =") {
65
&line[0..pos + 2]
66
} else {
67
return false;
68
};
69
70
// Ignore comments when copying lines.
71
if prefix.starts_with('#') {
72
return false;
73
}
74
// Need to decide a policy for these.
75
if prefix.starts_with("translators") {
76
return false;
77
}
78
let prefix = prefix.to_owned();
79
80
for iter_line in &self.lines {
81
if iter_line.starts_with(&prefix) {
82
// Already have it
83
return false;
84
}
85
}
86
87
// Now try to insert it at an alphabetic-ish location.
88
let prefix = prefix.to_ascii_lowercase();
89
90
// Then, find a suitable insertion spot
91
for (i, iter_line) in self.lines.iter().enumerate() {
92
if iter_line.to_ascii_lowercase() > prefix {
93
println!("Inserting line {} into {}", line, self.name);
94
self.lines.insert(i, line.to_owned());
95
return true;
96
}
97
}
98
99
for i in (0..self.lines.len()).rev() {
100
if self.lines[i].is_empty() {
101
continue;
102
}
103
println!("Inserting line {} into {}", line, self.name);
104
self.lines.insert(i + 1, line.to_owned());
105
return true;
106
}
107
108
println!("failed to insert {line}");
109
true
110
}
111
112
pub fn rename_key(&mut self, old: &str, new: &str) {
113
let prefix = old.to_owned() + " =";
114
let mut found_index = None;
115
for (index, line) in self.lines.iter().enumerate() {
116
if line.starts_with(&prefix) {
117
found_index = Some(index);
118
}
119
}
120
if let Some(index) = found_index {
121
let line = self.lines.remove(index);
122
let mut right_part = line.strip_prefix(&prefix).unwrap().to_string();
123
if right_part.trim() == old.trim() {
124
// Was still untranslated - replace the translation too.
125
right_part = format!(" {new}");
126
}
127
let line = new.to_owned() + " =" + &right_part;
128
self.insert_line_if_missing(&line);
129
} else {
130
let name = &self.name;
131
println!("rename_key: didn't find a line starting with {prefix} in section {name}");
132
}
133
}
134
135
pub fn dupe_key(&mut self, old: &str, new: &str) {
136
let prefix = old.to_owned() + " =";
137
let mut found_index = None;
138
for (index, line) in self.lines.iter().enumerate() {
139
if line.starts_with(&prefix) {
140
found_index = Some(index);
141
}
142
}
143
if let Some(index) = found_index {
144
let line = self.lines.get(index).unwrap();
145
let mut right_part = line.strip_prefix(&prefix).unwrap().to_string();
146
if right_part.trim() == old.trim() {
147
// Was still untranslated - replace the translation too.
148
right_part = format!(" {new}");
149
}
150
let line = new.to_owned() + " =" + &right_part;
151
self.insert_line_if_missing(&line);
152
} else {
153
let name = &self.name;
154
println!("dupe_key: didn't find a line starting with {prefix} in section {name}");
155
}
156
}
157
158
pub fn sort(&mut self) {
159
self.lines.sort();
160
}
161
162
pub fn comment_out_lines_if_not_in(&mut self, other: &Section) {
163
// Brute force (O(n^2)). Bad but not a problem.
164
165
for line in &mut self.lines {
166
let prefix = if let Some(pos) = line.find(" =") {
167
&line[0..pos + 2]
168
} else {
169
// Keep non-key lines.
170
continue;
171
};
172
if prefix.starts_with("Font") || prefix.starts_with('#') {
173
continue;
174
}
175
if !other.lines.iter().any(|line| line.starts_with(prefix)) && !prefix.contains("URL") {
176
println!("Commenting out from {}: {line}", other.name);
177
// Comment out the line.
178
*line = "#".to_owned() + line;
179
}
180
}
181
}
182
183
pub fn remove_lines_if_not_in(&mut self, other: &Section) {
184
// Brute force (O(n^2)). Bad but not a problem.
185
186
self.lines.retain(|line| {
187
let prefix = if let Some(pos) = line.find(" =") {
188
&line[0..pos + 2]
189
} else {
190
// Keep non-key lines.
191
return true;
192
};
193
if prefix.starts_with("Font") || prefix.starts_with('#') {
194
return true;
195
}
196
197
// keeps the line if this expression returns true.
198
other.lines.iter().any(|line| line.starts_with(prefix))
199
});
200
}
201
202
pub fn get_keys_if_not_in(&mut self, other: &Section) -> Vec<String> {
203
let mut missing_lines = Vec::new();
204
// Brute force (O(n^2)). Bad but not a problem.
205
for line in &self.lines {
206
let prefix = if let Some(pos) = line.find(" =") {
207
&line[0..pos + 2]
208
} else {
209
// Keep non-key lines.
210
continue;
211
};
212
if prefix.starts_with("Font") || prefix.starts_with('#') {
213
continue;
214
}
215
216
// keeps the line if this expression returns true.
217
if !other.lines.iter().any(|line| line.starts_with(prefix)) {
218
missing_lines.push(prefix[0..prefix.len() - 2].trim().to_string());
219
}
220
}
221
missing_lines
222
}
223
224
// Returns true if the key was found and updated.
225
pub fn set_value(&mut self, key: &str, value: &str) -> bool {
226
let mut found_index = None;
227
for (index, line) in self.lines.iter().enumerate() {
228
let prefix = if let Some(pos) = line.find(" =") {
229
&line[0..pos]
230
} else {
231
continue;
232
};
233
234
if prefix.eq_ignore_ascii_case(key) {
235
found_index = Some(index);
236
break;
237
}
238
}
239
240
if let Some(found_index) = found_index {
241
self.lines[found_index] = format!("{key} = {value}");
242
true
243
} else {
244
false
245
}
246
}
247
}
248
249