sounding_bufkit/
bufkit_data.rs1use std::collections::HashMap;
3use std::error::Error;
4use std::path::Path;
5
6mod combine;
7mod surface;
8mod surface_section;
9mod upper_air;
10mod upper_air_section;
11
12use sounding_analysis::Sounding;
13
14use self::surface_section::{SurfaceIterator, SurfaceSection};
15use self::upper_air_section::{UpperAirIterator, UpperAirSection};
16use crate::error::*;
17
18pub struct BufkitFile {
20 file_text: String,
21 file_name: String,
22}
23
24impl BufkitFile {
25 pub fn load(path: &Path) -> Result<BufkitFile, Box<dyn Error>> {
27 use std::fs::File;
28 use std::io::prelude::Read;
29 use std::io::BufReader;
30
31 let mut file = BufReader::new(File::open(path)?);
33 let mut contents = String::new();
34 file.read_to_string(&mut contents)?;
35
36 Ok(BufkitFile {
37 file_text: contents,
38 file_name: path
39 .file_name()
40 .map(|s| s.to_string_lossy().to_string())
41 .unwrap_or_else(|| "Unknown File".to_owned()),
42 })
43 }
44
45 pub fn validate_file_format(&self) -> Result<(), Box<dyn Error>> {
47 let data = self.data()?;
48 data.validate()?;
49
50 Ok(())
51 }
52
53 pub fn data(&self) -> Result<BufkitData<'_>, Box<dyn Error>> {
55 BufkitData::init(&self.file_text, &self.file_name)
56 }
57
58 pub fn raw_text(&self) -> &str {
60 &self.file_text
61 }
62}
63
64pub struct BufkitData<'a> {
69 upper_air: UpperAirSection<'a>,
70 surface: SurfaceSection<'a>,
71 file_name: &'a str,
72}
73
74impl<'a> BufkitData<'a> {
75 pub fn validate(&self) -> Result<(), Box<dyn Error>> {
77 self.upper_air.validate_section()?;
78 self.surface.validate_section()?;
79 Ok(())
80 }
81
82 pub fn init(text: &'a str, fname: &'a str) -> Result<BufkitData<'a>, Box<dyn Error>> {
84 let break_point = BufkitData::find_break_point(text)?;
85 let data = BufkitData::new_with_break_point(text, break_point, fname)?;
86 Ok(data)
87 }
88
89 fn new_with_break_point(
90 text: &'a str,
91 break_point: usize,
92 fname: &'a str,
93 ) -> Result<BufkitData<'a>, BufkitFileError> {
94 Ok(BufkitData {
95 upper_air: UpperAirSection::new(&text[0..break_point]),
96 surface: SurfaceSection::init(&text[break_point..])?,
97 file_name: fname,
98 })
99 }
100
101 fn find_break_point(text: &str) -> Result<usize, BufkitFileError> {
102 match text.find("STN YYMMDD/HHMM") {
103 None => Err(BufkitFileError::new()),
104 Some(val) => Ok(val),
105 }
106 }
107}
108
109impl<'a> IntoIterator for &'a BufkitData<'a> {
110 type Item = (Sounding, HashMap<&'static str, f64>);
111 type IntoIter = SoundingIterator<'a>;
112
113 fn into_iter(self) -> Self::IntoIter {
114 SoundingIterator {
115 upper_air_it: self.upper_air.into_iter(),
116 surface_it: self.surface.into_iter(),
117 source_name: self.file_name,
118 }
119 }
120}
121
122pub struct SoundingIterator<'a> {
124 upper_air_it: UpperAirIterator<'a>,
125 surface_it: SurfaceIterator<'a>,
126 source_name: &'a str,
127}
128
129impl<'a> Iterator for SoundingIterator<'a> {
130 type Item = (Sounding, HashMap<&'static str, f64>);
131
132 fn next(&mut self) -> Option<Self::Item> {
133 let mut next_ua = self.upper_air_it.next()?;
134 let mut next_sd = self.surface_it.next()?;
135
136 loop {
137 while next_sd.valid_time < next_ua.valid_time {
138 next_sd = self.surface_it.next()?;
139 }
140 while next_ua.valid_time < next_sd.valid_time {
141 next_ua = self.upper_air_it.next()?;
142 }
143 if next_ua.valid_time == next_sd.valid_time {
144 return Some(combine::combine_data(next_ua, next_sd, &self.source_name));
145 }
146 }
147 }
148}