Skip to main content

yazi_shared/data/
data.rs

1use std::borrow::Cow;
2
3use anyhow::{Result, anyhow, bail};
4use hashbrown::HashMap;
5use serde::{Deserialize, Serialize};
6
7use crate::{Id, SStr, data::{DataAny, DataKey}, path::PathBufDyn, strand::{IntoStrand, StrandBuf}, url::{Url, UrlBuf, UrlCow}};
8
9// --- Data
10#[derive(Clone, Debug, Default, Deserialize, Serialize)]
11#[serde(untagged)]
12pub enum Data {
13	#[default]
14	Nil,
15	Boolean(bool),
16	Integer(i64),
17	Number(f64),
18	String(SStr),
19	List(Vec<Self>),
20	Dict(HashMap<DataKey, Self>),
21	Id(Id),
22	#[serde(skip_deserializing)]
23	Url(UrlBuf),
24	#[serde(skip)]
25	Path(PathBufDyn),
26	#[serde(skip)]
27	Bytes(Vec<u8>),
28	#[serde(skip)]
29	Any(Box<dyn DataAny>),
30}
31
32impl From<()> for Data {
33	fn from(_: ()) -> Self { Self::Nil }
34}
35
36impl From<bool> for Data {
37	fn from(value: bool) -> Self { Self::Boolean(value) }
38}
39
40impl From<i32> for Data {
41	fn from(value: i32) -> Self { Self::Integer(value as i64) }
42}
43
44impl From<i64> for Data {
45	fn from(value: i64) -> Self { Self::Integer(value) }
46}
47
48impl From<f64> for Data {
49	fn from(value: f64) -> Self { Self::Number(value) }
50}
51
52impl From<usize> for Data {
53	fn from(value: usize) -> Self { Self::Id(value.into()) }
54}
55
56impl From<String> for Data {
57	fn from(value: String) -> Self { Self::String(Cow::Owned(value)) }
58}
59
60impl From<SStr> for Data {
61	fn from(value: SStr) -> Self { Self::String(value) }
62}
63
64impl From<Id> for Data {
65	fn from(value: Id) -> Self { Self::Id(value) }
66}
67
68impl From<Url<'_>> for Data {
69	fn from(value: Url) -> Self { Self::Url(value.into()) }
70}
71
72impl From<UrlBuf> for Data {
73	fn from(value: UrlBuf) -> Self { Self::Url(value) }
74}
75
76impl From<&UrlBuf> for Data {
77	fn from(value: &UrlBuf) -> Self { Self::Url(value.clone()) }
78}
79
80impl From<&str> for Data {
81	fn from(value: &str) -> Self { Self::String(Cow::Owned(value.to_owned())) }
82}
83
84impl<T> FromIterator<T> for Data
85where
86	T: Into<Data>,
87{
88	fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
89		Self::List(iter.into_iter().map(Into::into).collect())
90	}
91}
92
93impl TryFrom<&Data> for bool {
94	type Error = anyhow::Error;
95
96	fn try_from(value: &Data) -> Result<Self, Self::Error> {
97		match value {
98			Data::Boolean(b) => Ok(*b),
99			Data::String(s) if s == "no" => Ok(false),
100			Data::String(s) if s == "yes" => Ok(true),
101			_ => bail!("not a boolean"),
102		}
103	}
104}
105
106impl<'a> TryFrom<&'a Data> for &'a str {
107	type Error = anyhow::Error;
108
109	fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
110		match value {
111			Data::String(s) => Ok(s),
112			_ => bail!("not a string"),
113		}
114	}
115}
116
117impl TryFrom<Data> for SStr {
118	type Error = anyhow::Error;
119
120	fn try_from(value: Data) -> Result<Self, Self::Error> {
121		value.into_sstr().ok_or_else(|| anyhow!("not a string"))
122	}
123}
124
125impl<'a> TryFrom<&'a Data> for Cow<'a, str> {
126	type Error = anyhow::Error;
127
128	fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
129		match value {
130			Data::String(s) => Ok(Cow::Borrowed(s)),
131			_ => bail!("not a string"),
132		}
133	}
134}
135
136impl TryFrom<&Data> for String {
137	type Error = anyhow::Error;
138
139	fn try_from(value: &Data) -> Result<Self, Self::Error> {
140		value.try_into().map(|s: &str| s.to_owned())
141	}
142}
143
144impl TryFrom<Data> for String {
145	type Error = anyhow::Error;
146
147	fn try_from(value: Data) -> Result<Self, Self::Error> {
148		SStr::try_from(value).map(|s| s.into_owned())
149	}
150}
151
152impl TryFrom<&Data> for HashMap<DataKey, Data> {
153	type Error = anyhow::Error;
154
155	fn try_from(_: &Data) -> Result<Self, Self::Error> {
156		bail!("cannot take ownership of dict from &Data");
157	}
158}
159
160impl TryFrom<Data> for HashMap<DataKey, Data> {
161	type Error = anyhow::Error;
162
163	fn try_from(value: Data) -> Result<Self, Self::Error> {
164		match value {
165			Data::Dict(d) => Ok(d),
166			_ => bail!("not a dict"),
167		}
168	}
169}
170
171impl TryFrom<Data> for UrlCow<'static> {
172	type Error = anyhow::Error;
173
174	fn try_from(value: Data) -> Result<Self, Self::Error> {
175		match value {
176			Data::String(s) => s.try_into(),
177			Data::Url(u) => Ok(u.into()),
178			Data::Bytes(b) => b.try_into(),
179			_ => bail!("not a URL"),
180		}
181	}
182}
183
184impl TryFrom<Data> for UrlBuf {
185	type Error = anyhow::Error;
186
187	fn try_from(value: Data) -> Result<Self, Self::Error> { UrlCow::try_from(value).map(Into::into) }
188}
189
190impl<'a> TryFrom<&'a Data> for UrlCow<'a> {
191	type Error = anyhow::Error;
192
193	fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
194		match value {
195			Data::String(s) => Self::try_from(&**s),
196			Data::Url(u) => Ok(u.into()),
197			Data::Bytes(b) => b.as_slice().try_into(),
198			_ => bail!("not a URL"),
199		}
200	}
201}
202
203impl TryFrom<&Data> for UrlBuf {
204	type Error = anyhow::Error;
205
206	fn try_from(value: &Data) -> Result<Self, Self::Error> { UrlCow::try_from(value).map(Into::into) }
207}
208
209impl<'a> TryFrom<&'a Data> for &'a [u8] {
210	type Error = anyhow::Error;
211
212	fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
213		match value {
214			Data::Bytes(b) => Ok(b),
215			_ => bail!("not bytes"),
216		}
217	}
218}
219
220impl TryFrom<Data> for StrandBuf {
221	type Error = anyhow::Error;
222
223	fn try_from(value: Data) -> Result<Self, Self::Error> {
224		Ok(match value {
225			Data::String(s) => s.into_owned().into(),
226			Data::Path(p) => p.into_strand(),
227			Data::Bytes(b) => Self::Bytes(b),
228			_ => bail!("cannot convert to StrandBuf"),
229		})
230	}
231}
232
233impl TryFrom<&Data> for StrandBuf {
234	type Error = anyhow::Error;
235
236	fn try_from(value: &Data) -> Result<Self, Self::Error> {
237		Ok(match value {
238			Data::String(s) => s.to_string().into(),
239			Data::Path(p) => p.into_strand(),
240			Data::Bytes(b) => Self::Bytes(b.clone()),
241			_ => bail!("cannot convert to StrandBuf"),
242		})
243	}
244}
245
246impl PartialEq<bool> for Data {
247	fn eq(&self, other: &bool) -> bool { self.try_into().is_ok_and(|b| *other == b) }
248}
249
250impl Data {
251	pub fn as_str(&self) -> Option<&str> {
252		match self {
253			Self::String(s) => Some(s),
254			_ => None,
255		}
256	}
257
258	pub fn as_any<T: 'static>(&self) -> Option<&T> {
259		match self {
260			Self::Any(b) => (**b).as_any().downcast_ref::<T>(),
261			_ => None,
262		}
263	}
264
265	pub fn into_sstr(self) -> Option<SStr> {
266		match self {
267			Self::String(s) => Some(s),
268			_ => None,
269		}
270	}
271
272	pub fn into_any<T: 'static>(self) -> Option<T> {
273		match self {
274			Self::Any(b) => b.into_any().downcast::<T>().ok().map(|b| *b),
275			_ => None,
276		}
277	}
278
279	// FIXME: find a better name
280	pub fn into_any2<T: 'static>(self) -> Result<T> {
281		if let Self::Any(b) = self
282			&& let Ok(t) = b.into_any().downcast::<T>()
283		{
284			Ok(*t)
285		} else {
286			bail!("Failed to downcast Data into {}", std::any::type_name::<T>())
287		}
288	}
289}
290
291impl_into_integer!(Data, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, crate::Id);
292impl_into_number!(Data, f32, f64);