1use std::fmt::{self, Display, Formatter};
27
28use amplify::confinement::{SmallVec, TinyBlob, TinyString};
29use encoding::{FieldName, STRICT_TYPES_LIB};
30
31use crate::value::{EnumTag, StrictNum};
32use crate::StrictVal;
33
34#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
36#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
37#[strict_type(lib = STRICT_TYPES_LIB, tags = order, dumb = Self::Number(strict_dumb!()))]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
39#[non_exhaustive]
40pub enum KeyStep {
41 #[from]
42 Number(u128),
43
44 #[from]
45 TinyBlob(TinyBlob),
46
47 #[from]
48 TinyString(TinyString),
49}
50
51impl KeyStep {
52 pub fn has_match(&self, val: &StrictVal) -> bool {
53 match (self, val) {
54 (KeyStep::Number(no), StrictVal::Enum(EnumTag::Ord(tag))) if *tag as u128 == *no => {
55 true
56 }
57 (KeyStep::Number(num1), StrictVal::Number(StrictNum::Uint(num2)))
58 if *num1 == *num2 as u128 =>
59 {
60 true
61 }
62 (KeyStep::TinyBlob(blob1), StrictVal::Bytes(blob2))
63 if blob1.as_slice() == blob2.as_slice() =>
64 {
65 true
66 }
67 (KeyStep::TinyString(s1), StrictVal::String(s2)) if s1.as_str() == s2.as_str() => true,
68 _ => false,
69 }
70 }
71}
72
73impl Display for KeyStep {
74 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
75 match self {
76 KeyStep::Number(num) => Display::fmt(num, f),
77 KeyStep::TinyBlob(data) => {
78 f.write_str("0h")?;
79 for byte in data {
80 write!(f, "{byte:02X}")?;
81 }
82 Ok(())
83 }
84 KeyStep::TinyString(s) => {
85 let s = s.replace('"', "\\\"");
86 f.write_str(&s)
87 }
88 }
89 }
90}
91
92#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)]
93#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
94#[strict_type(lib = STRICT_TYPES_LIB, tags = order, dumb = Self::UnnamedField(strict_dumb!()))]
95#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
96pub enum Step {
97 #[display(".{0}")]
98 #[from]
99 NamedField(FieldName),
100
101 #[display(".{0}")]
102 #[from]
103 UnnamedField(u8),
104
105 #[display("[{0}]")]
106 Index(u32),
107
108 #[display("{{{0}}}")]
109 #[from]
110 Key(KeyStep),
111}
112
113#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)]
114#[wrapper(Deref)]
115#[wrapper_mut(DerefMut)]
116#[derive(StrictType, StrictEncode, StrictDecode)]
117#[strict_type(lib = STRICT_TYPES_LIB)]
118#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
119pub struct Path(SmallVec<Step>);
120
121impl Path {
122 pub fn new() -> Path { Path::default() }
123
124 pub fn with(step: Step) -> Path { Path(small_vec!(step)) }
125
126 pub fn iter(&self) -> std::slice::Iter<Step> { self.0.iter() }
127}
128
129impl<'path> IntoIterator for &'path Path {
130 type Item = &'path Step;
131 type IntoIter = std::slice::Iter<'path, Step>;
132
133 fn into_iter(self) -> Self::IntoIter { self.0.iter() }
134}
135
136impl Display for Path {
137 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
138 for step in self {
139 Display::fmt(step, f)?;
140 }
141 Ok(())
142 }
143}
144
145#[derive(Clone, PartialEq, Eq, Debug, Display, Error)]
148#[display(doc_comments)]
149pub enum PathError {
150 CollectionIndexOutOfBounds(u32, usize),
152 FieldNoOutOfBounds(u8, usize),
154 UnknownFieldName(FieldName),
156 UnknownKey(KeyStep),
158 TypeMismatch(Step, StrictVal),
160}
161
162impl StrictVal {
163 pub fn at_path<'p>(
164 &self,
165 path: impl IntoIterator<Item = &'p Step>,
166 ) -> Result<&StrictVal, PathError> {
167 let mut iter = path.into_iter();
168 match (self, iter.next()) {
169 (val, None) => Ok(val),
170 (StrictVal::Tuple(fields), Some(Step::UnnamedField(no)))
171 if *no as usize >= fields.len() =>
172 {
173 Err(PathError::FieldNoOutOfBounds(*no, fields.len()))
174 }
175 (StrictVal::Tuple(fields), Some(Step::UnnamedField(no))) => Ok(&fields[*no as usize]),
176 (StrictVal::Struct(fields), Some(Step::NamedField(name))) => {
177 fields.get(name).ok_or(PathError::UnknownFieldName(name.clone()))
178 }
179 (StrictVal::List(items) | StrictVal::Set(items), Some(Step::Index(idx)))
180 if *idx as usize >= items.len() =>
181 {
182 Err(PathError::CollectionIndexOutOfBounds(*idx, items.len()))
183 }
184 (StrictVal::List(items) | StrictVal::Set(items), Some(Step::Index(idx))) => {
185 Ok(&items[*idx as usize])
186 }
187 (StrictVal::Map(items), Some(Step::Key(idx))) => items
188 .iter()
189 .find(|(key, _)| idx.has_match(key))
190 .map(|(_, val)| val)
191 .ok_or(PathError::UnknownKey(idx.clone())),
192
193 (_, Some(step)) => Err(PathError::TypeMismatch(step.clone(), self.clone())),
194 }
195 }
196}