1use super::*;
3use crate::agent::AgentID;
4use crate::asset::Asset;
5use crate::id::IDCollection;
6use crate::process::ProcessMap;
7use crate::region::RegionID;
8use anyhow::{Context, Result};
9use itertools::Itertools;
10use serde::Deserialize;
11use std::collections::HashSet;
12use std::path::Path;
13use std::rc::Rc;
14
15const ASSETS_FILE_NAME: &str = "assets.csv";
16
17#[derive(Deserialize, PartialEq)]
18struct AssetRaw {
19 process_id: String,
20 region_id: String,
21 agent_id: String,
22 capacity: f64,
23 commission_year: u32,
24}
25
26pub fn read_assets(
39 model_dir: &Path,
40 agent_ids: &HashSet<AgentID>,
41 processes: &ProcessMap,
42 region_ids: &HashSet<RegionID>,
43) -> Result<Vec<Asset>> {
44 let file_path = model_dir.join(ASSETS_FILE_NAME);
45 let assets_csv = read_csv(&file_path)?;
46 read_assets_from_iter(assets_csv, agent_ids, processes, region_ids)
47 .with_context(|| input_err_msg(&file_path))
48}
49
50fn read_assets_from_iter<I>(
63 iter: I,
64 agent_ids: &HashSet<AgentID>,
65 processes: &ProcessMap,
66 region_ids: &HashSet<RegionID>,
67) -> Result<Vec<Asset>>
68where
69 I: Iterator<Item = AssetRaw>,
70{
71 iter.map(|asset| -> Result<_> {
72 let agent_id = agent_ids.get_id_by_str(&asset.agent_id)?;
73 let process = processes
74 .get(asset.process_id.as_str())
75 .with_context(|| format!("Invalid process ID: {}", &asset.process_id))?;
76 let region_id = region_ids.get_id_by_str(&asset.region_id)?;
77
78 Asset::new(
79 agent_id,
80 Rc::clone(process),
81 region_id,
82 asset.capacity,
83 asset.commission_year,
84 )
85 })
86 .try_collect()
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::fixture::{processes, region_ids};
93
94 use itertools::assert_equal;
95 use rstest::{fixture, rstest};
96 use std::iter;
97
98 #[fixture]
99 fn agent_ids() -> HashSet<AgentID> {
100 iter::once("agent1".into()).collect()
101 }
102
103 #[rstest]
104 fn test_read_assets_from_iter_valid(
105 agent_ids: HashSet<AgentID>,
106 processes: ProcessMap,
107 region_ids: HashSet<RegionID>,
108 ) {
109 let asset_in = AssetRaw {
110 agent_id: "agent1".into(),
111 process_id: "process1".into(),
112 region_id: "GBR".into(),
113 capacity: 1.0,
114 commission_year: 2010,
115 };
116 let asset_out = Asset::new(
117 "agent1".into(),
118 Rc::clone(processes.values().next().unwrap()),
119 "GBR".into(),
120 1.0,
121 2010,
122 )
123 .unwrap();
124 assert_equal(
125 read_assets_from_iter(iter::once(asset_in), &agent_ids, &processes, ®ion_ids)
126 .unwrap(),
127 iter::once(asset_out),
128 );
129 }
130
131 #[rstest]
132 #[case(AssetRaw { agent_id: "agent1".into(),
134 process_id: "process2".into(),
135 region_id: "GBR".into(),
136 capacity: 1.0,
137 commission_year: 2010,
138 })]
139 #[case(AssetRaw { agent_id: "agent2".into(),
141 process_id: "process1".into(),
142 region_id: "GBR".into(),
143 capacity: 1.0,
144 commission_year: 2010,
145 })]
146 #[case(AssetRaw { agent_id: "agent1".into(),
148 process_id: "process1".into(),
149 region_id: "FRA".into(),
150 capacity: 1.0,
151 commission_year: 2010,
152 })]
153 fn test_read_assets_from_iter_invalid(
154 #[case] asset: AssetRaw,
155 agent_ids: HashSet<AgentID>,
156 processes: ProcessMap,
157 region_ids: HashSet<RegionID>,
158 ) {
159 assert!(
160 read_assets_from_iter(iter::once(asset), &agent_ids, &processes, ®ion_ids).is_err()
161 );
162 }
163}