cargo_toml/
inheritable.rs

1use std::collections::BTreeMap;
2
3use crate::Error;
4use serde::{Deserialize, Serialize, Serializer};
5
6/// Placeholder for a property that may be missing from its package, and needs to be copied from a `Workspace`.
7#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
8#[serde(untagged, try_from = "InheritableSerdeParser<T>")]
9pub enum Inheritable<T> {
10    Set(T),
11    #[serde(serialize_with = "workspace_true")]
12    Inherited,
13}
14
15impl<T> TryFrom<InheritableSerdeParser<T>> for Inheritable<T> {
16    type Error = String;
17    fn try_from(parsed: InheritableSerdeParser<T>) -> Result<Self, String> {
18        match parsed {
19            InheritableSerdeParser::Set(v) => Ok(Self::Set(v)),
20            InheritableSerdeParser::Inherited { workspace: true } => Ok(Self::Inherited),
21            InheritableSerdeParser::Inherited { workspace: false } => Err("inherited field with `workspace = false` is not allowed".into()),
22            InheritableSerdeParser::ParseErrorFallback(s) => Err(format!("Error parsing field content. Expected to deserialize {}, found {s}", std::any::type_name::<T>())),
23        }
24    }
25}
26
27fn workspace_true<S: Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
28    #[derive(Serialize)]
29    struct Inherited {
30        workspace: bool,
31    }
32    Inherited { workspace: true }.serialize(serializer)
33}
34
35#[derive(Deserialize)]
36#[serde(untagged)]
37pub enum InheritableSerdeParser<T> {
38    Set(T),
39    Inherited {
40        /// Always `true` (this is for serde)
41        workspace: bool,
42    },
43    ParseErrorFallback(toml::Value),
44}
45
46impl<T: PartialEq> PartialEq for Inheritable<T> {
47    fn eq(&self, other: &Self) -> bool {
48        match (self, other) {
49            (Self::Set(a), Self::Set(b)) => a.eq(b),
50            _ => false,
51        }
52    }
53}
54
55impl<T> Inheritable<T> {
56    pub fn as_ref(&self) -> Inheritable<&T> {
57        match self {
58            Self::Set(t) => Inheritable::Set(t),
59            Self::Inherited => Inheritable::Inherited,
60        }
61    }
62
63    /// You can read the value
64    pub fn is_set(&self) -> bool {
65        matches!(self, Self::Set(_))
66    }
67
68    pub fn get(&self) -> Result<&T, Error> {
69        match self {
70            Self::Set(t) => Ok(t),
71            Self::Inherited => Err(Error::InheritedUnknownValue),
72        }
73    }
74
75    pub fn set(&mut self, val: T) {
76        *self = Self::Set(val);
77    }
78
79    pub fn as_mut(&mut self) -> Inheritable<&mut T> {
80        match self {
81            Self::Set(t) => Inheritable::Set(t),
82            Self::Inherited => Inheritable::Inherited,
83        }
84    }
85
86    /// Fails if inherited
87    pub fn get_mut(&mut self) -> Result<&mut T, Error> {
88        match self {
89            Self::Set(t) => Ok(t),
90            Self::Inherited => Err(Error::InheritedUnknownValue),
91        }
92    }
93
94    /// Panics if inherited
95    #[track_caller]
96    pub fn unwrap(self) -> T {
97        match self {
98            Self::Set(t) => t,
99            Self::Inherited => panic!("inherited workspace value"),
100        }
101    }
102
103    /// Copy from workspace if needed
104    pub fn inherit(&mut self, other: &T) where T: Clone {
105        if let Self::Inherited = self {
106            *self = Self::Set(other.clone());
107        }
108    }
109}
110
111impl<T: Default> Default for Inheritable<T> {
112    fn default() -> Self {
113        Self::Set(T::default())
114    }
115}
116
117impl<T> Inheritable<Vec<T>> {
118    /// False if inherited and unknown
119    #[must_use]
120    pub fn is_empty(&self) -> bool {
121        match self {
122            Self::Inherited => false,
123            Self::Set(v) => v.is_empty(),
124        }
125    }
126}
127
128impl<K, V> Inheritable<BTreeMap<K, V>> {
129    /// False if inherited and unknown
130    #[must_use]
131    pub fn is_empty(&self) -> bool {
132        match self {
133            Self::Inherited => false,
134            Self::Set(v) => v.is_empty(),
135        }
136    }
137}
138
139impl<T: Default + PartialEq> Inheritable<T> {
140    /// False if inherited and unknown
141    pub fn is_default(&self) -> bool {
142        match self {
143            Self::Inherited => false,
144            Self::Set(v) => T::default() == *v,
145        }
146    }
147}
148
149impl<T> From<Option<T>> for Inheritable<T> {
150    /// Inherits if `None`
151    fn from(val: Option<T>) -> Self {
152        match val {
153            Some(val) => Self::Set(val),
154            None => Self::Inherited,
155        }
156    }
157}
158
159impl<T> From<Inheritable<T>> for Option<T> {
160    /// `None` if inherited
161    fn from(val: Inheritable<T>) -> Self {
162        match val {
163            Inheritable::Inherited => None,
164            Inheritable::Set(val) => Some(val),
165        }
166    }
167}
168
169#[test]
170fn serializes() {
171    #[derive(Serialize)]
172    struct Foo {
173        bar: Inheritable<&'static str>,
174    }
175    let s = toml::to_string(&Foo {
176        bar: Inheritable::Inherited,
177    }).unwrap();
178    assert_eq!(s, "[bar]\nworkspace = true\n");
179
180    let s = toml::to_string(&Foo {
181        bar: Inheritable::Set("hello"),
182    }).unwrap();
183    assert_eq!(s, "bar = \"hello\"\n");
184}