1use crate::cjerror::CjError as Error;
2use cjseq::{CityJSON, CityJSONFeature};
3use std::io::{BufRead, BufReader, Read};
4
5pub struct CityJSONSeq {
6 pub cj: CityJSON,
7 pub features: Vec<CityJSONFeature>,
8}
9
10pub enum CJType {
11 Normal(CityJSON),
12 Seq(CityJSONSeq),
13}
14
15#[derive(Debug)]
16pub enum CJTypeKind {
17 Normal,
18 Seq,
19}
20
21pub trait CityJSONReader {
22 fn read_lines(&mut self) -> Box<dyn Iterator<Item = Result<String, Error>> + '_>;
23}
24
25impl<R: Read> CityJSONReader for BufReader<R> {
26 fn read_lines(&mut self) -> Box<dyn Iterator<Item = Result<String, Error>> + '_> {
27 Box::new(self.lines().map(|line| line.map_err(Error::Io)))
28 }
29}
30
31impl CityJSONReader for &str {
32 fn read_lines(&mut self) -> Box<dyn Iterator<Item = Result<String, Error>> + '_> {
33 match std::fs::File::open(self) {
34 Ok(file) => Box::new(
35 BufReader::new(file)
36 .lines()
37 .map(|line| line.map_err(Error::Io)),
38 ),
39 Err(e) => Box::new(std::iter::once(Err(Error::Io(e)))),
40 }
41 }
42}
43
44fn parse_cityjson<T: CityJSONReader>(mut source: T, cj_type: CJTypeKind) -> Result<CJType, Error> {
45 let mut lines = source.read_lines();
46
47 match cj_type {
48 CJTypeKind::Normal => {
49 let content = lines.collect::<Result<Vec<_>, Error>>()?.join("\n");
50
51 let cj: CityJSON = serde_json::from_str(&content)?;
52 Ok(CJType::Normal(cj))
53 }
54
55 CJTypeKind::Seq => {
56 let first_line = lines
58 .next()
59 .ok_or(Error::Io(std::io::Error::other("Empty input")))?;
60 let cj: CityJSON = serde_json::from_str(&first_line?)?;
61
62 let features: Result<Vec<_>, Error> = lines
64 .map(|line| -> Result<_, Error> {
65 let line = line?;
66 Ok(serde_json::from_str(&line)?)
67 })
68 .collect();
69
70 Ok(CJType::Seq(CityJSONSeq {
71 cj,
72 features: features?,
73 }))
74 }
75 }
76}
77
78pub fn read_cityjson(file: &str, cj_type: CJTypeKind) -> Result<CJType, Error> {
80 parse_cityjson(file, cj_type)
81}
82
83pub fn read_cityjson_from_reader<R: Read>(
85 reader: BufReader<R>,
86 cj_type: CJTypeKind,
87) -> Result<CJType, Error> {
88 parse_cityjson(reader, cj_type)
89}
90
91#[cfg(test)]
105mod tests {
106 use std::{fs::File, path::PathBuf};
107
108 use super::*;
109
110 #[test]
111 fn test_read_from_memory() -> Result<(), Error> {
112 let input_file = BufReader::new(File::open(
113 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/small.city.jsonl"),
114 )?);
115 let result = read_cityjson_from_reader(input_file, CJTypeKind::Seq)?;
116
117 if let CJType::Seq(seq) = result {
118 assert_eq!(seq.features.len(), 3);
119 } else {
120 panic!("Expected Seq type");
121 }
122
123 Ok(())
124 }
125}