#![allow(missing_docs)]
use crate::agent::Agent;
use crate::commodity::Commodity;
use crate::input::*;
use crate::process::Process;
use crate::region::Region;
use crate::time_slice::TimeSliceInfo;
use anyhow::{ensure, Context, Result};
use serde::Deserialize;
use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
const MODEL_FILE_NAME: &str = "model.toml";
pub struct Model {
pub milestone_years: Vec<u32>,
pub agents: HashMap<Rc<str>, Agent>,
pub commodities: HashMap<Rc<str>, Rc<Commodity>>,
pub processes: HashMap<Rc<str>, Rc<Process>>,
pub time_slice_info: TimeSliceInfo,
pub regions: HashMap<Rc<str>, Region>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct ModelFile {
pub milestone_years: MilestoneYears,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct MilestoneYears {
pub years: Vec<u32>,
}
fn check_milestone_years(years: &[u32]) -> Result<()> {
ensure!(!years.is_empty(), "`milestone_years` is empty");
ensure!(
years[..years.len() - 1]
.iter()
.zip(years[1..].iter())
.all(|(y1, y2)| y1 < y2),
"`milestone_years` must be composed of unique values in order"
);
Ok(())
}
impl ModelFile {
pub fn from_path<P: AsRef<Path>>(model_dir: P) -> Result<ModelFile> {
let file_path = model_dir.as_ref().join(MODEL_FILE_NAME);
let model_file: ModelFile = read_toml(&file_path)?;
check_milestone_years(&model_file.milestone_years.years)
.with_context(|| input_err_msg(file_path))?;
Ok(model_file)
}
}
impl Model {
pub fn iter_years(&self) -> impl Iterator<Item = u32> + '_ {
self.milestone_years.iter().copied()
}
pub fn iter_regions(&self) -> impl Iterator<Item = &Rc<str>> + '_ {
self.regions.keys()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io::Write;
use tempfile::tempdir;
#[test]
fn test_check_milestone_years() {
assert!(check_milestone_years(&[1]).is_ok());
assert!(check_milestone_years(&[1, 2]).is_ok());
assert!(check_milestone_years(&[]).is_err());
assert!(check_milestone_years(&[1, 1]).is_err());
assert!(check_milestone_years(&[2, 1]).is_err());
}
#[test]
fn test_model_file_from_path() {
let dir = tempdir().unwrap();
{
let mut file = File::create(dir.path().join(MODEL_FILE_NAME)).unwrap();
writeln!(file, "[milestone_years]\nyears = [2020, 2100]").unwrap();
}
let model_file = ModelFile::from_path(dir.path()).unwrap();
assert_eq!(model_file.milestone_years.years, vec![2020, 2100]);
}
}