weavatrix_edit/
provenance.rs1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2
3#[derive(Clone, Debug, Eq, Hash, PartialEq)]
5pub struct Provenance(ProvenanceValue);
6
7#[derive(Clone, Debug, Eq, Hash, PartialEq)]
8enum ProvenanceValue {
9 ExactLsp,
10 Resolved,
11 Extracted,
12 LexicalExact,
13 #[allow(clippy::box_collection)]
16 Other(Box<String>),
17}
18
19impl Provenance {
20 pub const EXACT_LSP: &'static str = "EXACT_LSP";
21 pub const RESOLVED: &'static str = "RESOLVED";
22 pub const EXTRACTED: &'static str = "EXTRACTED";
23 pub const LEXICAL_EXACT: &'static str = "LEXICAL_EXACT";
24
25 #[must_use]
26 pub fn new(value: impl AsRef<str>) -> Self {
27 let value = value.as_ref();
28 Self(
29 known_value(value)
30 .unwrap_or_else(|| ProvenanceValue::Other(Box::new(value.to_owned()))),
31 )
32 }
33
34 fn from_owned(value: String) -> Self {
35 Self(known_value(&value).unwrap_or_else(|| ProvenanceValue::Other(Box::new(value))))
36 }
37
38 #[must_use]
39 pub fn as_str(&self) -> &str {
40 match &self.0 {
41 ProvenanceValue::ExactLsp => Self::EXACT_LSP,
42 ProvenanceValue::Resolved => Self::RESOLVED,
43 ProvenanceValue::Extracted => Self::EXTRACTED,
44 ProvenanceValue::LexicalExact => Self::LEXICAL_EXACT,
45 ProvenanceValue::Other(value) => value,
46 }
47 }
48
49 #[must_use]
50 pub const fn is_applicable(&self) -> bool {
51 !matches!(self.0, ProvenanceValue::Other(_))
52 }
53}
54
55fn known_value(value: &str) -> Option<ProvenanceValue> {
56 match value {
57 Provenance::EXACT_LSP => Some(ProvenanceValue::ExactLsp),
58 Provenance::RESOLVED => Some(ProvenanceValue::Resolved),
59 Provenance::EXTRACTED => Some(ProvenanceValue::Extracted),
60 Provenance::LEXICAL_EXACT => Some(ProvenanceValue::LexicalExact),
61 _ => None,
62 }
63}
64
65impl Serialize for Provenance {
66 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67 where
68 S: Serializer,
69 {
70 serializer.serialize_str(self.as_str())
71 }
72}
73
74impl<'de> Deserialize<'de> for Provenance {
75 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76 where
77 D: Deserializer<'de>,
78 {
79 String::deserialize(deserializer).map(Self::from_owned)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::Provenance;
86
87 #[test]
88 fn known_values_are_compact_and_unknown_values_roundtrip() {
89 assert!(core::mem::size_of::<Provenance>() < core::mem::size_of::<String>());
90 assert!(Provenance::new(Provenance::EXACT_LSP).is_applicable());
91
92 let unknown = Provenance::new("FUTURE_TIER");
93 assert!(!unknown.is_applicable());
94 let encoded = blazingly_json::to_string(&unknown).unwrap();
95 assert_eq!(encoded, r#""FUTURE_TIER""#);
96 assert_eq!(
97 blazingly_json::from_str::<Provenance>(&encoded).unwrap(),
98 unknown
99 );
100 }
101}