1use anyhow::Result;
4use indexmap::IndexMap;
5use std::{path::PathBuf, str::FromStr};
6
7#[derive(Debug, Clone, Default)]
9#[cfg_attr(feature = "serde", derive(serde_derive::Deserialize))]
10#[cfg_attr(
11 feature = "serde",
12 serde(rename_all = "kebab-case", deny_unknown_fields)
13)]
14pub struct Dependency {
15 pub path: PathBuf,
17}
18
19impl FromStr for Dependency {
20 type Err = ();
21
22 fn from_str(s: &str) -> Result<Self, Self::Err> {
23 Ok(Self { path: s.into() })
24 }
25}
26
27#[derive(Debug, Clone, Default)]
29#[cfg_attr(feature = "serde", derive(serde_derive::Deserialize))]
30#[cfg_attr(
31 feature = "serde",
32 serde(rename_all = "kebab-case", deny_unknown_fields)
33)]
34pub struct InstantiationArg {
35 pub instance: String,
37
38 #[cfg_attr(feature = "serde", serde(default))]
42 pub export: Option<String>,
43}
44
45impl FromStr for InstantiationArg {
46 type Err = ();
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 Ok(Self {
50 instance: s.to_string(),
51 export: None,
52 })
53 }
54}
55
56#[derive(Debug, Clone, Default)]
58#[cfg_attr(feature = "serde", derive(serde_derive::Deserialize))]
59#[cfg_attr(
60 feature = "serde",
61 serde(rename_all = "kebab-case", deny_unknown_fields)
62)]
63pub struct Instantiation {
64 pub dependency: Option<String>,
68
69 #[cfg_attr(feature = "serde", serde(default, deserialize_with = "de::index_map"))]
74 pub arguments: IndexMap<String, InstantiationArg>,
75}
76
77#[derive(Default, Debug, Clone)]
79#[cfg_attr(feature = "serde", derive(serde_derive::Deserialize))]
80#[cfg_attr(
81 feature = "serde",
82 serde(rename_all = "kebab-case", deny_unknown_fields)
83)]
84pub struct Config {
85 #[cfg_attr(feature = "serde", serde(skip))]
89 pub dir: PathBuf,
90
91 #[cfg_attr(feature = "serde", serde(default))]
93 pub definitions: Vec<PathBuf>,
94
95 #[cfg_attr(feature = "serde", serde(default))]
99 pub search_paths: Vec<PathBuf>,
100
101 #[cfg_attr(feature = "serde", serde(default))]
103 pub skip_validation: bool,
104
105 #[cfg_attr(feature = "serde", serde(default))]
110 pub import_components: bool,
111
112 #[cfg_attr(feature = "serde", serde(default))]
117 pub disallow_imports: bool,
118
119 #[cfg_attr(feature = "serde", serde(default, deserialize_with = "de::index_map"))]
121 pub dependencies: IndexMap<String, Dependency>,
122
123 #[cfg_attr(feature = "serde", serde(default))]
125 pub instantiations: IndexMap<String, Instantiation>,
126}
127
128impl Config {
129 #[cfg(feature = "yaml")]
131 pub fn from_file(path: impl Into<PathBuf>) -> Result<Self> {
132 use anyhow::Context;
133 use std::path::Path;
134
135 let path = path.into();
136
137 log::info!("reading configuration file `{}`", path.display());
138
139 let config = std::fs::read_to_string(&path)
140 .with_context(|| format!("failed to read configuration file `{}`", path.display()))?;
141
142 let mut config: Config = serde_yaml2::from_str(&config)
143 .with_context(|| format!("failed to parse configuration file `{}`", path.display()))?;
144
145 config.dir = path.parent().map(Path::to_path_buf).unwrap_or_default();
146
147 Ok(config)
148 }
149
150 pub fn dependency_name<'a>(&'a self, instance: &'a str) -> &'a str {
152 self.instantiations
153 .get(instance)
154 .and_then(|i| i.dependency.as_deref())
155 .unwrap_or(instance)
156 }
157}
158
159#[cfg(feature = "serde")]
160mod de {
161 use indexmap::IndexMap;
162 use serde::{
163 Deserialize, Deserializer,
164 de::{self, MapAccess, Visitor},
165 };
166 use std::{fmt, hash::Hash, marker::PhantomData, str::FromStr};
167
168 pub fn index_map<'de, K, V, D>(deserializer: D) -> Result<IndexMap<K, V>, D::Error>
171 where
172 K: Hash + Eq + Deserialize<'de>,
173 V: Deserialize<'de> + FromStr<Err = ()>,
174 D: Deserializer<'de>,
175 {
176 deserializer.deserialize_map(MapVisitor(PhantomData))
177 }
178
179 struct MapVisitor<K, V>(PhantomData<(K, V)>);
180
181 impl<'de, K, V> Visitor<'de> for MapVisitor<K, V>
182 where
183 K: Hash + Eq + Deserialize<'de>,
184 V: Deserialize<'de> + FromStr<Err = ()>,
185 {
186 type Value = IndexMap<K, V>;
187
188 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
189 formatter.write_str("map")
190 }
191
192 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
193 where
194 M: MapAccess<'de>,
195 {
196 struct Wrapper<V>(V);
197
198 impl<'de, V> Deserialize<'de> for Wrapper<V>
199 where
200 V: Deserialize<'de> + FromStr<Err = ()>,
201 {
202 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203 where
204 D: Deserializer<'de>,
205 {
206 Ok(Self(
207 deserializer.deserialize_any(StringOrMapVisitor(PhantomData))?,
208 ))
209 }
210 }
211
212 let mut map = Self::Value::with_capacity(access.size_hint().unwrap_or(0));
213 while let Some((key, value)) = access.next_entry::<_, Wrapper<V>>()? {
214 map.insert(key, value.0);
215 }
216
217 Ok(map)
218 }
219 }
220
221 struct StringOrMapVisitor<V>(PhantomData<V>);
222
223 impl<'de, V> Visitor<'de> for StringOrMapVisitor<V>
224 where
225 V: Deserialize<'de> + FromStr<Err = ()>,
226 {
227 type Value = V;
228
229 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
230 formatter.write_str("string or map")
231 }
232
233 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
234 where
235 E: de::Error,
236 {
237 Ok(V::from_str(value).unwrap())
238 }
239
240 fn visit_map<M>(self, access: M) -> Result<Self::Value, M::Error>
241 where
242 M: MapAccess<'de>,
243 {
244 Deserialize::deserialize(de::value::MapAccessDeserializer::new(access))
245 }
246 }
247}