cargo_toml/
inheritable.rs1use 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
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 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 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 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 #[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 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 #[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 #[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 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 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 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}