cargo_toml/
inheritable.rs
1use std::collections::BTreeMap;
2
3use crate::Error;
4use serde::{Deserialize, Serialize, Serializer};
5
6#[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 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 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 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 #[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 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 #[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 #[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 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 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 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}