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