cargo/config/
config_store.rs1use std::fs;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context as _, bail};
5
6use super::{ConfigKey, ConfigValue, Definition, DefinitionKind};
7use crate::util::data_structures::HashMap;
8use crate::util::errors::CargoResult;
9
10use ConfigValue as CV;
11
12#[derive(Default, Debug)]
13pub struct ConfigStore {
14 values: HashMap<String, ConfigValue>,
16 def: Definition,
17}
18
19impl ConfigStore {
20 pub fn with_values(values: HashMap<String, ConfigValue>) -> Self {
21 Self {
22 values,
23 def: Definition::BuiltIn,
24 }
25 }
26
27 pub fn with_table_value(value: ConfigValue) -> Option<Self> {
28 match value {
29 CV::Table(values, def) => Some(Self { values, def }),
30 _ => None,
31 }
32 }
33
34 pub fn read_layer(path: &Path, definition_kind: DefinitionKind) -> CargoResult<Self> {
36 tracing::debug!(?path, ?definition_kind, "load config from file");
37
38 let contents = fs::read_to_string(path)
39 .with_context(|| format!("failed to read configuration file `{}`", path.display()))?;
40 let toml = contents.parse::<toml::Table>().with_context(|| {
41 format!("could not parse TOML configuration in `{}`", path.display())
42 })?;
43 let def = match definition_kind {
44 DefinitionKind::Cli => Definition::Cli(Some(path.into())),
45 DefinitionKind::Path => Definition::Path(path.into()),
46 DefinitionKind::BuiltIn => Definition::BuiltIn,
47 DefinitionKind::Environment => {
48 unimplemented!("Environment defiinitions are not supported")
49 }
50 };
51 let value = CV::from_toml(def, toml::Value::Table(toml)).with_context(|| {
52 format!(
53 "failed to load TOML configuration from `{}`",
54 path.display()
55 )
56 })?;
57 Ok(Self::with_table_value(value).unwrap())
58 }
59
60 pub fn into_value(self) -> ConfigValue {
61 let Self { values, def } = self;
62 CV::Table(values, def)
63 }
64
65 pub fn definition(&self) -> &Definition {
66 &self.def
67 }
68
69 pub fn merge(&mut self, from: Self, force: bool) -> CargoResult<()> {
78 let mut root_cv = std::mem::take(self).into_value();
79 let from = from.into_value();
80 root_cv.merge(from, force)?;
81 *self = Self::with_table_value(root_cv).unwrap();
82
83 Ok(())
84 }
85
86 pub fn values(&self) -> &HashMap<String, ConfigValue> {
87 &self.values
88 }
89
90 pub fn values_mut(&mut self) -> &mut HashMap<String, ConfigValue> {
91 &mut self.values
92 }
93
94 pub fn get_cv(&self, key: &ConfigKey) -> CargoResult<Option<ConfigValue>> {
99 let vals = self.values();
100 tracing::trace!("get cv {:?}", key);
101 if key.is_root() {
102 return Ok(Some(CV::Table(
105 vals.clone(),
106 Definition::Path(PathBuf::new()),
107 )));
108 }
109 let mut parts = key.parts().enumerate();
110 let Some(mut val) = vals.get(parts.next().unwrap().1) else {
111 return Ok(None);
112 };
113 for (i, part) in parts {
114 match val {
115 CV::Table(map, _) => {
116 val = match map.get(part) {
117 Some(val) => val,
118 None => return Ok(None),
119 }
120 }
121 CV::Integer(_, def)
122 | CV::String(_, def)
123 | CV::List(_, def)
124 | CV::Boolean(_, def) => {
125 let key_so_far = ConfigKey::from_parts(key.parts().take(i));
126 bail!(
127 "expected table for configuration key `{}`, \
128 but found {} in {}",
129 key_so_far,
130 val.desc(),
131 def
132 )
133 }
134 }
135 }
136 Ok(Some(val.clone()))
137 }
138}