use std::path::Path;
use nom::{IResult, Parser, character::char, combinator::map, sequence::preceded};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
JourneyId,
error::{HResult, HrdfError},
models::ExchangeTimeJourney,
parsing::{
error::{PResult, ParsingError},
helpers::{
i16_from_n_digits_parser, i32_from_n_digits_parser, optional_i32_from_n_digits_parser,
read_lines, string_from_n_chars_parser,
},
},
storage::ResourceStorage,
utils::AutoIncrement,
};
type ExchangeTimeJourneyLine = (i32, i32, String, i32, String, i16, bool, Option<i32>);
fn parse_exchange_journey_row(input: &str) -> IResult<&str, ExchangeTimeJourneyLine> {
let (
res,
(
stop_id,
journey_id_1,
administration_1,
journey_id_2,
administration_2,
duration,
is_guaranteed,
bitfield_id,
),
) = (
i32_from_n_digits_parser(7),
preceded(char(' '), i32_from_n_digits_parser(6)),
preceded(char(' '), string_from_n_chars_parser(6)),
preceded(char(' '), i32_from_n_digits_parser(6)),
preceded(char(' '), string_from_n_chars_parser(6)),
preceded(char(' '), i16_from_n_digits_parser(3)),
map(string_from_n_chars_parser(1), |s| s == "!"),
preceded(char(' '), optional_i32_from_n_digits_parser(6)),
)
.parse(input)?;
Ok((
res,
(
stop_id,
journey_id_1,
administration_1,
journey_id_2,
administration_2,
duration,
is_guaranteed,
bitfield_id,
),
))
}
fn parse_line(
line: &str,
auto_increment: &AutoIncrement,
journeys_pk_type_converter: &FxHashSet<JourneyId>,
) -> PResult<(i32, ExchangeTimeJourney)> {
let (
_,
(
stop_id,
journey_id_1,
administration_1,
journey_id_2,
administration_2,
duration,
is_guaranteed,
bitfield_id,
),
) = parse_exchange_journey_row(line)?;
let _journey_id_1 = journeys_pk_type_converter
.get(&(journey_id_1, administration_1.clone()))
.ok_or_else(|| ParsingError::UnknownId(format!("({journey_id_1}, {administration_1})")))?;
let _journey_id_2 = journeys_pk_type_converter
.get(&(journey_id_2, administration_2.clone()))
.ok_or_else(|| ParsingError::UnknownId(format!("({journey_id_2}, {administration_2})")))?;
let id = auto_increment.next();
Ok((
id,
ExchangeTimeJourney::new(
id,
stop_id,
(journey_id_1, administration_1),
(journey_id_2, administration_2),
duration,
is_guaranteed,
bitfield_id,
),
))
}
pub fn parse(
path: &Path,
journeys_pk_type_converter: &FxHashSet<JourneyId>,
) -> HResult<ResourceStorage<ExchangeTimeJourney>> {
log::info!("Parsing UMSTEIGZ...");
let file = path.join("UMSTEIGZ");
let lines = read_lines(&file, 0)?;
let auto_increment = AutoIncrement::new();
let exchanges = lines
.into_iter()
.enumerate()
.filter(|(_, line)| !line.trim().is_empty())
.map(|(line_number, line)| {
parse_line(&line, &auto_increment, journeys_pk_type_converter).map_err(|e| {
HrdfError::Parsing {
error: e,
file: String::from(file.to_string_lossy()),
line,
line_number,
}
})
})
.collect::<HResult<FxHashMap<i32, ExchangeTimeJourney>>>()?;
Ok(ResourceStorage::new(exchanges))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parsing::tests::get_json_values;
use pretty_assertions::assert_eq;
#[test]
fn row_parser() {
let line = "8501008 023057 000011 001671 000011 002 000010 Genève";
let (
_res,
(
stop_id,
journey_id_1,
administration_1,
journey_id_2,
administration_2,
duration,
is_guaranteed,
bit_field_id,
),
) = parse_exchange_journey_row(line).unwrap();
assert_eq!(8501008, stop_id);
assert_eq!(23057, journey_id_1);
assert_eq!("000011", &administration_1);
assert_eq!(1671, journey_id_2);
assert_eq!("000011", &administration_2);
assert_eq!(2, duration);
assert!(!is_guaranteed);
assert_eq!(Some(10), bit_field_id);
let line = "8501120 001929 000011 024256 000011 999 Lausanne";
let (
_res,
(
stop_id,
journey_id_1,
administration_1,
journey_id_2,
administration_2,
duration,
is_guaranteed,
bit_field_id,
),
) = parse_exchange_journey_row(line).unwrap();
assert_eq!(8501120, stop_id);
assert_eq!(1929, journey_id_1);
assert_eq!("000011", &administration_1);
assert_eq!(24256, journey_id_2);
assert_eq!("000011", &administration_2);
assert_eq!(999, duration);
assert!(!is_guaranteed);
assert_eq!(None, bit_field_id);
let line = "8575489 000020 000801 000045 000801 004! 000019 Crana, Ponte Oscuro";
let (
_res,
(
stop_id,
journey_id_1,
administration_1,
journey_id_2,
administration_2,
duration,
is_guaranteed,
bit_field_id,
),
) = parse_exchange_journey_row(line).unwrap();
assert_eq!(8575489, stop_id);
assert_eq!(20, journey_id_1);
assert_eq!("000801", &administration_1);
assert_eq!(45, journey_id_2);
assert_eq!("000801", &administration_2);
assert_eq!(4, duration);
assert!(is_guaranteed);
assert_eq!(Some(19), bit_field_id);
}
#[test]
fn multiple_row_parsing() {
let lines = vec![
"8501008 023057 000011 001671 000011 002 000010 Genève".to_string(),
"8501120 001929 000011 024256 000011 999 Lausanne".to_string(),
"8575489 000020 000801 000045 000801 004! 000019 Crana, Ponte Oscuro".to_string(),
];
let mut journeys_pk_type_converter: FxHashSet<JourneyId> = FxHashSet::default();
journeys_pk_type_converter.insert((23057, "000011".to_string()));
journeys_pk_type_converter.insert((1929, "000011".to_string()));
journeys_pk_type_converter.insert((1671, "000011".to_string()));
journeys_pk_type_converter.insert((24256, "000011".to_string()));
journeys_pk_type_converter.insert((20, "000801".to_string()));
journeys_pk_type_converter.insert((45, "000801".to_string()));
let auto_increment = AutoIncrement::new();
let exchanges = lines
.into_iter()
.filter(|line| !line.trim().is_empty())
.map(|line| parse_line(&line, &auto_increment, &journeys_pk_type_converter))
.collect::<PResult<FxHashMap<_, _>>>()
.unwrap();
let attribute = exchanges.get(&1).unwrap();
let reference = r#"
{
"id":1,
"stop_id": 8501008,
"journey_legacy_id_1": 23057,
"administration_1": "000011",
"journey_legacy_id_2": 1671,
"administration_2": "000011",
"duration": 2,
"is_guaranteed": false,
"bit_field_id": 10
}"#;
let (attribute, reference) = get_json_values(attribute, reference).unwrap();
assert_eq!(attribute, reference);
let attribute = exchanges.get(&2).unwrap();
let reference = r#"
{
"id":2,
"stop_id": 8501120,
"journey_legacy_id_1": 1929,
"administration_1": "000011",
"journey_legacy_id_2": 24256,
"administration_2": "000011",
"duration": 999,
"is_guaranteed": false,
"bit_field_id": null
}"#;
let (attribute, reference) = get_json_values(attribute, reference).unwrap();
assert_eq!(attribute, reference);
let attribute = exchanges.get(&3).unwrap();
let reference = r#"
{
"id":3,
"stop_id": 8575489,
"journey_legacy_id_1": 20,
"administration_1": "000801",
"journey_legacy_id_2": 45,
"administration_2": "000801",
"duration": 4,
"is_guaranteed": true,
"bit_field_id": 19
}"#;
let (attribute, reference) = get_json_values(attribute, reference).unwrap();
assert_eq!(attribute, reference);
}
}