Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Tools/langtool/src/inifile.rs
5852 views
1
use std::fs::File;
2
use std::io::{self, BufRead, Write};
3
use std::path::{Path, PathBuf};
4
5
use crate::section::Section;
6
7
#[derive(Debug, Clone)]
8
pub struct IniFile {
9
pub filename: Option<PathBuf>,
10
pub preamble: Vec<String>,
11
pub sections: Vec<Section>,
12
pub has_bom: bool,
13
}
14
15
// Grabbed from a sample, a fast line reader iterator.
16
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
17
where
18
P: AsRef<Path>,
19
{
20
let file = File::open(filename)?;
21
use std::io::BufRead;
22
Ok(io::BufReader::new(file).lines())
23
}
24
25
fn read_lines_from_string(s: &str) -> io::Result<io::Lines<io::BufReader<&[u8]>>> {
26
Ok(io::BufReader::new(s.as_bytes()).lines())
27
}
28
29
impl IniFile {
30
pub fn parse_file(filename: &str) -> io::Result<IniFile> {
31
let lines = read_lines(filename)?;
32
33
Self::parse_lines(lines, Some(PathBuf::from(filename)))
34
}
35
36
pub fn parse_string(s: &str) -> io::Result<IniFile> {
37
let lines = read_lines_from_string(s)?;
38
39
Self::parse_lines(lines, None)
40
}
41
42
pub fn parse_lines<R: io::BufRead>(
43
lines: io::Lines<R>,
44
filename: Option<PathBuf>,
45
) -> io::Result<IniFile> {
46
let mut sections = vec![];
47
let mut preamble = vec![];
48
let mut cur_section = None;
49
50
let mut has_bom = false;
51
52
for line in lines {
53
let line = line.unwrap();
54
55
let line = if let Some(line) = line.strip_prefix('\u{feff}') {
56
has_bom = true;
57
line
58
} else {
59
&line
60
};
61
62
if let Some('[') = line.chars().next() {
63
if let Some(right_bracket) = line.find(']') {
64
if let Some(section) = cur_section.take() {
65
sections.push(section);
66
}
67
68
let name = &line[1..right_bracket];
69
cur_section = Some(Section {
70
name: name.to_owned(),
71
title_line: line.to_owned(), // preserves comment and bom
72
lines: vec![],
73
});
74
} else {
75
// Bad syntax
76
break;
77
}
78
} else if let Some(cur_section) = &mut cur_section {
79
cur_section.lines.push(line.to_owned());
80
} else {
81
preamble.push(line.to_owned());
82
}
83
}
84
85
if let Some(section) = cur_section.take() {
86
sections.push(section);
87
}
88
89
let ini = IniFile {
90
filename,
91
preamble,
92
sections,
93
has_bom,
94
};
95
Ok(ini)
96
}
97
98
pub fn write(&self) -> io::Result<()> {
99
let Some(filename) = &self.filename else {
100
return Err(io::Error::new(
101
io::ErrorKind::Other,
102
"No filename specified for writing",
103
));
104
};
105
let file = std::fs::File::create(filename)?;
106
let mut file = std::io::LineWriter::new(file);
107
108
// Write BOM
109
if self.has_bom {
110
file.write_all("\u{feff}".as_bytes())?;
111
}
112
for line in &self.preamble {
113
file.write_all(line.as_bytes())?;
114
file.write_all(b"\n")?;
115
}
116
for section in &self.sections {
117
file.write_all(section.title_line.as_bytes())?;
118
file.write_all(b"\n")?;
119
for line in &section.lines {
120
file.write_all(line.as_bytes())?;
121
file.write_all(b"\n")?;
122
}
123
}
124
125
Ok(())
126
}
127
128
// Assumes alphabetical section order!
129
pub fn insert_section_if_missing(&mut self, section: &Section) -> bool {
130
// First, check if it's there.
131
132
for iter_section in &self.sections {
133
if iter_section.name == section.name {
134
return false;
135
}
136
}
137
138
// Then, find a suitable insertion spot
139
for (i, iter_section) in self.sections.iter_mut().enumerate() {
140
if iter_section.name > section.name {
141
println!("Inserting section {}", section.name);
142
self.sections.insert(i, section.clone());
143
return true;
144
}
145
}
146
// Reached the end for some reason? Add it.
147
// Also add an empty line to the previous section.
148
if let Some(last) = self.sections.last_mut() {
149
last.lines.push("".into());
150
}
151
self.sections.push(section.clone());
152
true
153
}
154
155
pub fn get_section_mut(&mut self, section_name: &str) -> Option<&mut Section> {
156
self.sections
157
.iter_mut()
158
.find(|section| section.name == section_name)
159
}
160
161
pub fn get_section(&self, section_name: &str) -> Option<&Section> {
162
self.sections
163
.iter()
164
.find(|section| section.name == section_name)
165
}
166
}
167
168