sct_reader/loaders/euroscope/
reader.rs

1use std::{
2    collections::HashMap,
3    fs::File,
4    io::{BufRead, BufReader, BufWriter},
5    time::Instant,
6};
7
8use super::{
9    colour::Colour,
10    error::Error,
11    partial::{ArtccOrAirwayLineType, BeaconType, PartialSector, SidStarType},
12    sector::Sector,
13    SectorResult,
14};
15use std::io::Write;
16
17pub struct SctReader<R: BufRead> {
18    source: R,
19    current_section: FileSection,
20    partial_sector: PartialSector,
21    errors: Vec<(usize, String, Error)>,
22}
23impl<R: BufRead> SctReader<R> {
24    pub fn new(source: R) -> Self {
25        Self {
26            source,
27            current_section: FileSection::ColourDefinitions,
28            partial_sector: PartialSector::new(),
29            errors: vec![],
30        }
31    }
32
33    pub fn try_read(mut self) -> SectorResult<Sector> {
34        for (mut line_number, line) in self.source.lines().enumerate() {
35            if let Ok(line) = line {
36                let mut line = line.trim_end();
37                line_number += 1;
38
39                if line.is_empty() || line.starts_with(';') {
40                    continue;
41                }
42                if line.contains(';') {
43                    let mut line_split = line.split(';');
44                    line = line_split.next().unwrap().trim_end();
45                }
46                if line.starts_with('[') {
47                    match parse_file_section(line) {
48                        Ok(new_section) => self.current_section = new_section,
49                        Err(e) => self.errors.push((line_number + 1, line.to_owned(), e)),
50                    }
51                    continue;
52                }
53                if line.starts_with("OFFSET") {
54                    
55                }
56                if line.starts_with("#define") {
57                    self.current_section = FileSection::ColourDefinitions;
58                }
59
60                let result = match self.current_section {
61                    FileSection::ColourDefinitions => self.partial_sector.parse_colour_line(line),
62                    FileSection::Info => self.partial_sector.parse_sector_info_line(line),
63                    FileSection::Airport => self.partial_sector.parse_airport_line(line),
64                    FileSection::Runway => self.partial_sector.parse_runway_line(line),
65                    FileSection::Vor => self
66                        .partial_sector
67                        .parse_vor_or_ndb_line(line, BeaconType::Vor),
68                    FileSection::Ndb => self
69                        .partial_sector
70                        .parse_vor_or_ndb_line(line, BeaconType::Ndb),
71                    FileSection::Fixes => self.partial_sector.parse_fixes_line(line),
72                    FileSection::Artcc => self
73                        .partial_sector
74                        .parse_artcc_or_airway_line(line, ArtccOrAirwayLineType::Artcc),
75                    FileSection::ArtccHigh => self
76                        .partial_sector
77                        .parse_artcc_or_airway_line(line, ArtccOrAirwayLineType::ArtccHigh),
78                    FileSection::ArtccLow => self
79                        .partial_sector
80                        .parse_artcc_or_airway_line(line, ArtccOrAirwayLineType::ArtccLow),
81                    FileSection::LowAirway => self
82                        .partial_sector
83                        .parse_artcc_or_airway_line(line, ArtccOrAirwayLineType::LowAirway),
84                    FileSection::HighAirway => self
85                        .partial_sector
86                        .parse_artcc_or_airway_line(line, ArtccOrAirwayLineType::HighAirway),
87                    FileSection::Sid => self
88                        .partial_sector
89                        .parse_sid_star_line(line, SidStarType::Sid),
90                    FileSection::Star => self
91                        .partial_sector
92                        .parse_sid_star_line(line, SidStarType::Star),
93                    FileSection::Geo => self.partial_sector.parse_geo_line(line),
94                    FileSection::Regions => self.partial_sector.parse_region_line(line),
95                    FileSection::Labels => self.partial_sector.parse_label_line(line),
96                };
97                if let Err(e) = result {
98                    self.errors.push((line_number + 1, line.to_owned(), e));
99                }
100            }
101        }
102
103        let mut sector: Sector = self.partial_sector.try_into()?;
104        sector.non_critical_errors = self.errors;
105        Ok(sector)
106    }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110enum FileSection {
111    ColourDefinitions,
112    Info,
113    Vor,
114    Ndb,
115    Airport,
116    Runway,
117    Fixes,
118    Geo,
119    LowAirway,
120    HighAirway,
121    Artcc,
122    ArtccHigh,
123    ArtccLow,
124    Sid,
125    Star,
126    Regions,
127    Labels,
128}
129
130fn parse_file_section(value: &str) -> SectorResult<FileSection> {
131    let new_section = match value.to_uppercase().as_str() {
132        "[INFO]" => FileSection::Info,
133        "[AIRPORT]" => FileSection::Airport,
134        "[VOR]" => FileSection::Vor,
135        "[NDB]" => FileSection::Ndb,
136        "[RUNWAY]" => FileSection::Runway,
137        "[FIXES]" => FileSection::Fixes,
138        "[ARTCC]" => FileSection::Artcc,
139        "[ARTCC HIGH]" => FileSection::ArtccHigh,
140        "[ARTCC LOW]" => FileSection::ArtccLow,
141        "[SID]" => FileSection::Sid,
142        "[STAR]" => FileSection::Star,
143        "[LOW AIRWAY]" => FileSection::LowAirway,
144        "[HIGH AIRWAY]" => FileSection::HighAirway,
145        "[GEO]" => FileSection::Geo,
146        "[REGIONS]" => FileSection::Regions,
147        "[LABELS]" => FileSection::Labels,
148        _ => return Err(Error::InvalidFileSection),
149    };
150    Ok(new_section)
151}
152
153#[test]
154#[ignore]
155fn test() {
156    let file =
157        File::open(r#"C:\Users\chpme\AppData\Roaming\EuroScope\UK\Data\Sector\UK_2023_11.sct"#)
158            .unwrap();
159    let reader = BufReader::new(file);
160    let sct_reader = SctReader::new(reader);
161    let timer = Instant::now();
162    match sct_reader.try_read() {
163        Ok(sector) => {
164            let elapsed = timer.elapsed();
165            let mut output = BufWriter::new(File::create("output.txt").unwrap());
166            writeln!(output, "Took {} ms", elapsed.as_millis()).unwrap();
167            write!(output, "{:#?}", sector).unwrap();
168            writeln!(output).unwrap();
169            for (line_number, line, error) in sector.non_critical_errors {
170                writeln!(output, "{} on line {}:", error, line_number).unwrap();
171                writeln!(output, "{}", line).unwrap();
172                writeln!(output).unwrap();
173            }
174        }
175        Err(error) => println!("{:#?}", error),
176    }
177}