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