mod bounds_map;
mod ranges_map;
mod rhs_map;
mod row_column_value_map;
mod row_type_map;
use crate::model::bounds_map::BoundsMap;
use crate::model::ranges_map::RangesMap;
use crate::model::rhs_map::RhsMap;
use crate::model::row_column_value_map::RowColumnValueMap;
use crate::model::row_type_map::RowTypeMap;
use crate::types::Parser;
use color_eyre::Result;
use fast_float2::FastFloat;
use hashbrown::HashSet;
#[cfg(feature = "serde")]
use serde::Serialize;
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Model<T: FastFloat> {
pub name: String,
pub row_types: RowTypeMap,
pub values: RowColumnValueMap<T>,
pub rhs: RhsMap<T>,
pub bounds: BoundsMap<T>,
pub ranges: RangesMap<T>,
}
impl<T: FastFloat> TryFrom<Parser<'_, T>> for Model<T> {
type Error = color_eyre::Report;
fn try_from(parsed: Parser<T>) -> Result<Self> {
let row_types = RowTypeMap::try_from(&parsed.rows)?;
let values = RowColumnValueMap::try_from((&parsed.columns, &row_types))?;
let rhs = match parsed.rhs {
Some(rhs) => RhsMap::try_from((&rhs, &row_types)),
None => Ok(RhsMap::default()),
}?;
let mut column_names = HashSet::<&str>::new();
for c in &parsed.columns {
column_names.insert(c.name);
}
let bounds = match parsed.bounds {
Some(bounds) => BoundsMap::try_from((&bounds, &column_names)),
None => Ok(BoundsMap::default()),
}?;
let ranges = match parsed.ranges {
Some(ranges) => RangesMap::try_from((&ranges, &row_types)),
None => Ok(RangesMap::default()),
}?;
Ok(Model {
name: parsed.name.to_string(),
row_types,
values,
rhs,
bounds,
ranges,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use color_eyre::{eyre::eyre, Result};
#[test]
fn test_conflicting_ranges_line() -> Result<()> {
let parsed = Parser::<f32>::parse(include_str!(
"../../tests/data/should_fail/conflicting_ranges_line"
))?;
let error = eyre!(
"duplicate entry in RANGES \"RANGE1\" at row \"VILLKOR6\": found 2.5 and 2.5"
);
match Model::try_from(parsed) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.to_string(), error.to_string()),
};
Ok(())
}
#[test]
fn test_conflicting_bounds_line() -> Result<()> {
let parsed = Parser::<f32>::parse(include_str!(
"../../tests/data/should_fail/conflicting_bounds_line"
))?;
let error = eyre!(
"duplicate entry in BOUNDS \"BOUND\" for column \"UGTD03\": found Some(0.2) and Some(20.2)"
);
match Model::try_from(parsed) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.to_string(), error.to_string()),
};
Ok(())
}
#[test]
fn test_conflicting_rhs_line() -> Result<()> {
let parsed = Parser::<f32>::parse(include_str!(
"../../tests/data/should_fail/conflicting_rhs_line"
))?;
let error = eyre!(
"duplicate entry in RHS \"B\" at row \"X51\": found 120.0 and 300.0"
);
match Model::try_from(parsed) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.to_string(), error.to_string()),
};
Ok(())
}
#[test]
fn test_conflicting_rows_line() -> Result<()> {
let parsed = Parser::<f32>::parse(include_str!(
"../../tests/data/should_fail/conflicting_rows_line"
))?;
let error =
eyre!("conflicting row type information for R09: found Leq and Eq");
match Model::try_from(parsed) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.to_string(), error.to_string()),
};
Ok(())
}
#[test]
fn test_unspecified_row_type() -> Result<()> {
let parsed = Parser::<f32>::parse(include_str!(
"../../tests/data/should_fail/unspecified_row_type"
))?;
let error = eyre!("referenced row of unspecified type: X27");
match Model::try_from(parsed) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.to_string(), error.to_string()),
};
Ok(())
}
}