sct_reader/loaders/euroscope/
reader.rs1use 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}