Skip to content

Commit e1f9ea2

Browse files
committed
Support nested subtables (one-to-many relations)
1 parent 4f8b26f commit e1f9ea2

File tree

1 file changed

+56
-30
lines changed

1 file changed

+56
-30
lines changed

src/main.rs

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::io::{Read, Write, stdout};
55
use std::fs::File;
66
use std::path::Path;
77
use std::env;
8+
use std::cell::RefCell;
89
use std::time::Instant;
910
use quick_xml::Reader;
1011
use quick_xml::events::Event;
@@ -13,30 +14,34 @@ use yaml_rust::yaml::Yaml;
1314

1415
struct Table<'a> {
1516
path: String,
16-
file: Box<dyn Write>,
17+
file: RefCell<Box<dyn Write>>,
1718
columns: Vec<Column<'a>>
1819
}
1920
impl<'a> Table<'a> {
20-
fn new<'b>(path: &str, file: Option<&str>) -> Table<'b> {
21+
fn new(path: &str, file: Option<&str>) -> Table<'a> {
2122
Table {
2223
path: String::from(path),
2324
file: match file {
24-
None => Box::new(stdout()),
25-
Some(ref file) => Box::new(File::create(&Path::new(file)).unwrap())
25+
None => RefCell::new(Box::new(stdout())),
26+
Some(ref file) => RefCell::new(Box::new(File::create(&Path::new(file)).unwrap()))
2627
},
2728
columns: Vec::new()
2829
}
2930
}
31+
fn write(&self, text: &str) {
32+
self.file.borrow_mut().write_all(&text.as_bytes()).expect("Write error encountered; exiting...");
33+
}
3034
}
3135

3236
struct Column<'a> {
3337
name: String,
3438
path: String,
35-
value: String,
39+
value: RefCell<String>,
3640
convert: Option<&'a str>,
3741
search: Option<&'a str>,
3842
replace: Option<&'a str>,
39-
consol: Option<&'a str>
43+
consol: Option<&'a str>,
44+
subtable: Option<Table<'a>>
4045
}
4146

4247
struct Geometry {
@@ -57,7 +62,7 @@ impl Geometry {
5762
}
5863
}
5964

60-
fn gml_to_ewkb(value: &mut String, geom: &Geometry) {
65+
fn gml_to_ewkb(cell: &RefCell<String>, geom: &Geometry) {
6166
let mut ewkb = vec![1, geom.gtype, 0, 0];
6267
let code = match geom.dims {
6368
2 => 32,
@@ -76,25 +81,33 @@ fn gml_to_ewkb(value: &mut String, geom: &Geometry) {
7681
ewkb.extend_from_slice(&pos.to_le_bytes());
7782
}
7883
}
84+
let mut value = cell.borrow_mut();
7985
for byte in ewkb.iter() {
8086
value.push_str(&format!("{:02X}", byte));
8187
}
8288
}
8389

84-
fn add_table<'a>(tables: &mut Vec<Table<'a>>, rowpath: &str, outfile: Option<&str>, colspec: &'a Vec<Yaml>) {
85-
tables.push(Table::new(rowpath, outfile));
86-
let table = tables.last_mut().unwrap();
90+
fn add_table<'a>(rowpath: &str, outfile: Option<&str>, colspec: &'a Vec<Yaml>) -> Table<'a> {
91+
let mut table = Table::new(rowpath, outfile);
8792
for col in colspec {
8893
let name = col["name"].as_str().expect("Column has no 'name' entry in configuration file");
8994
let colpath = col["path"].as_str().expect("Column has no 'path' entry in configuration file");
95+
let mut path = String::from(rowpath);
96+
path.push_str(colpath);
97+
let subtable: Option<Table> = match col["cols"].is_badvalue() {
98+
true => None,
99+
false => {
100+
let file = col["file"].as_str().expect("Subtable has no 'file' entry");
101+
Some(add_table(&path, Some(&file), col["cols"].as_vec().expect("Subtable 'cols' entry is not an array")))
102+
}
103+
};
90104
let convert = col["convert"].as_str();
91105
let search = col["search"].as_str();
92106
let replace = col["replace"].as_str();
93107
let consol = col["consol"].as_str();
94-
let mut path = String::from(rowpath);
95-
path.push_str(colpath);
96-
table.columns.push(Column { name: name.to_string(), path, value: String::new(), convert, search, replace, consol });
108+
table.columns.push(Column { name: name.to_string(), path, value: RefCell::new(String::new()), convert, search, replace, consol, subtable });
97109
}
110+
table
98111
}
99112

100113
fn main() -> std::io::Result<()> {
@@ -104,9 +117,11 @@ fn main() -> std::io::Result<()> {
104117
return Ok(());
105118
}
106119

107-
let mut config_str = String::new();
108-
File::open(&args[1]).unwrap().read_to_string(&mut config_str).unwrap();
109-
let config = &YamlLoader::load_from_str(&config_str).unwrap()[0];
120+
let config = {
121+
let mut config_str = String::new();
122+
File::open(&args[1]).unwrap().read_to_string(&mut config_str).unwrap();
123+
&YamlLoader::load_from_str(&config_str).unwrap()[0]
124+
};
110125

111126
let mut reader;
112127
reader = Reader::from_file(&args[2]).unwrap();
@@ -119,9 +134,9 @@ fn main() -> std::io::Result<()> {
119134
let rowpath = config["path"].as_str().expect("No valid 'rowpath' entry in configuration file");
120135
let colspec = config["cols"].as_vec().expect("No valid 'columns' array in configuration file");
121136
let outfile = config["file"].as_str();
122-
let mut tables: Vec<Table> = Vec::new();
123-
add_table(&mut tables, rowpath, outfile, colspec);
124-
let table = tables.first_mut().unwrap();
137+
let maintable = add_table(rowpath, outfile, colspec);
138+
let mut tables: Vec<&Table> = Vec::new();
139+
let mut table = &maintable;
125140

126141
let mut xmltotext = false;
127142
let mut text = String::new();
@@ -188,6 +203,11 @@ fn main() -> std::io::Result<()> {
188203
else if path.len() > table.path.len() {
189204
for i in 0..table.columns.len() {
190205
if path == table.columns[i].path {
206+
if table.columns[i].subtable.is_some() {
207+
tables.push(table);
208+
table = &table.columns[i].subtable.as_ref().unwrap();
209+
break;
210+
}
191211
match table.columns[i].convert {
192212
None => (),
193213
Some("xml-to-text") => xmltotext = true,
@@ -217,38 +237,44 @@ fn main() -> std::io::Result<()> {
217237
if path == table.columns[i].path {
218238
match table.columns[i].consol {
219239
None => {
220-
if !table.columns[i].value.is_empty() {
240+
if !table.columns[i].value.borrow().is_empty() {
221241
eprintln!("Column '{}' has multiple occurrences without a consolidation method; using 'first'", table.columns[i].name);
222242
break;
223243
}
224244
},
225245
Some("append") => {
226-
if !table.columns[i].value.is_empty() { table.columns[i].value.push(','); }
246+
if !table.columns[i].value.borrow().is_empty() { table.columns[i].value.borrow_mut().push(','); }
227247
},
228248
Some(s) => {
229249
eprintln!("Column '{}' has invalid consolidation method {}", table.columns[i].name, s);
230250
break;
231251
}
232252
}
233-
table.columns[i].value.push_str(&e.unescape_and_decode(&reader).unwrap().replace("\\", "\\\\"));
253+
table.columns[i].value.borrow_mut().push_str(&e.unescape_and_decode(&reader).unwrap().replace("\\", "\\\\"));
234254
break;
235255
}
236256
}
237257
},
238258
Ok(Event::End(_)) => {
239259
if path == table.path {
260+
if !tables.is_empty() {
261+
table.write(&tables.last().unwrap().columns[0].value.borrow());
262+
table.write("\t");
263+
}
240264
for i in 0..table.columns.len() {
241-
if i > 0 { write!(table.file, "\t")?; }
242-
if table.columns[i].value.is_empty() { write!(table.file, "\\N")?; }
265+
if i > 0 { table.write("\t"); }
266+
if table.columns[i].value.borrow().is_empty() { table.write("\\N"); }
243267
else {
244268
if let (Some(s), Some(r)) = (table.columns[i].search, table.columns[i].replace) {
245-
table.columns[i].value = table.columns[i].value.replace(s, r);
269+
let mut value = table.columns[i].value.borrow_mut();
270+
*value = value.replace(s, r);
246271
}
247-
write!(table.file, "{}", table.columns[i].value)?;
248-
table.columns[i].value.clear();
272+
table.write(&table.columns[i].value.borrow());
273+
table.columns[i].value.borrow_mut().clear();
249274
}
250275
}
251-
writeln!(table.file)?;
276+
table.write("\n");
277+
if !tables.is_empty() { table = tables.pop().unwrap(); }
252278
}
253279
let i = path.rfind('/').unwrap();
254280
let tag = path.split_off(i);
@@ -260,7 +286,7 @@ fn main() -> std::io::Result<()> {
260286
if let (Some(s), Some(r)) = (table.columns[i].search, table.columns[i].replace) {
261287
text = text.replace(s, r);
262288
}
263-
table.columns[i].value.push_str(&text);
289+
table.columns[i].value.borrow_mut().push_str(&text);
264290
text.clear();
265291
break;
266292
}
@@ -270,7 +296,7 @@ fn main() -> std::io::Result<()> {
270296
for i in 0..table.columns.len() {
271297
if path == table.columns[i].path {
272298
gmltoewkb = false;
273-
gml_to_ewkb(&mut table.columns[i].value, &gmlgeom);
299+
gml_to_ewkb(&table.columns[i].value, &gmlgeom);
274300
gmlgeom.reset();
275301
break;
276302
}

0 commit comments

Comments
 (0)