1use crate::{
2 Bos, DefaultStr, IntoStatic,
3 types::{DataModelType, LexiconStringType, UriType, blob::Blob, string::*},
4};
5use alloc::boxed::Box;
6use alloc::collections::BTreeMap;
7use alloc::vec::Vec;
8use bytes::Bytes;
9use core::convert::Infallible;
10use ipld_core::ipld::Ipld;
11use serde::Serialize;
12use smol_str::{SmolStr, ToSmolStr};
13
14pub mod convert;
16pub mod parsing;
18pub mod serde_impl;
20
21pub use serde_impl::{DataDeserializerError, RawDataSerializerError};
22
23#[cfg(test)]
24mod tests;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum Data<S: Bos<str> + AsRef<str> = DefaultStr> {
35 Null,
37 Boolean(bool),
39 Integer(i64),
41 String(AtprotoStr<S>),
43 Bytes(Bytes),
45 CidLink(Cid<S>),
47 Array(Array<S>),
49 Object(Object<S>),
51 Blob(Blob<S>),
53 InvalidNumber(S),
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, miette::Diagnostic)]
59#[non_exhaustive]
60pub enum AtDataError {
61 #[error("floating point numbers not allowed in AT protocol data")]
63 FloatNotAllowed,
64 #[error("invalid data type for AT protocol data")]
66 InvalidType,
67 #[error("deserialization error")]
69 Deserialization,
70}
71
72impl<S> Data<S>
73where
74 S: Bos<str> + AsRef<str>,
75{
76 pub fn data_type(&self) -> DataModelType {
78 match self {
79 Data::Null => DataModelType::Null,
80 Data::Boolean(_) => DataModelType::Boolean,
81 Data::Integer(_) => DataModelType::Integer,
82 Data::String(s) => match s {
83 AtprotoStr::Datetime(_) => DataModelType::String(LexiconStringType::Datetime),
84 AtprotoStr::Language(_) => DataModelType::String(LexiconStringType::Language),
85 AtprotoStr::Tid(_) => DataModelType::String(LexiconStringType::Tid),
86 AtprotoStr::Nsid(_) => DataModelType::String(LexiconStringType::Nsid),
87 AtprotoStr::Did(_) => DataModelType::String(LexiconStringType::Did),
88 AtprotoStr::Handle(_) => DataModelType::String(LexiconStringType::Handle),
89 AtprotoStr::AtIdentifier(_) => {
90 DataModelType::String(LexiconStringType::AtIdentifier)
91 }
92 AtprotoStr::AtUri(_) => DataModelType::String(LexiconStringType::AtUri),
93 AtprotoStr::Uri(uri) => match uri {
94 UriValue::Did(_) => DataModelType::String(LexiconStringType::Uri(UriType::Did)),
95 UriValue::At(_) => DataModelType::String(LexiconStringType::Uri(UriType::At)),
96 UriValue::Https(_) => {
97 DataModelType::String(LexiconStringType::Uri(UriType::Https))
98 }
99 UriValue::Wss(_) => DataModelType::String(LexiconStringType::Uri(UriType::Wss)),
100 UriValue::Cid(_) => DataModelType::String(LexiconStringType::Uri(UriType::Cid)),
101 UriValue::Any(_) => DataModelType::String(LexiconStringType::Uri(UriType::Any)),
102 },
103 AtprotoStr::Cid(_) => DataModelType::String(LexiconStringType::Cid),
104 AtprotoStr::RecordKey(_) => DataModelType::String(LexiconStringType::RecordKey),
105 AtprotoStr::String(_) => DataModelType::String(LexiconStringType::String),
106 },
107 Data::Bytes(_) => DataModelType::Bytes,
108 Data::CidLink(_) => DataModelType::CidLink,
109 Data::Array(_) => DataModelType::Array,
110 Data::Object(_) => DataModelType::Object,
111 Data::Blob(_) => DataModelType::Blob,
112 Data::InvalidNumber(_) => DataModelType::Bytes,
113 }
114 }
115
116 pub fn as_object(&self) -> Option<&Object<S>> {
118 if let Data::Object(obj) = self {
119 Some(obj)
120 } else {
121 None
122 }
123 }
124
125 pub fn as_array(&self) -> Option<&Array<S>> {
127 if let Data::Array(arr) = self {
128 Some(arr)
129 } else {
130 None
131 }
132 }
133
134 pub fn as_str(&self) -> Option<&str> {
136 if let Data::String(s) = self {
137 Some(s.as_str())
138 } else {
139 None
140 }
141 }
142
143 pub fn as_object_mut<'a>(&'a mut self) -> Option<&'a mut Object<S>> {
145 if let Data::Object(obj) = self {
146 Some(obj)
147 } else {
148 None
149 }
150 }
151
152 pub fn as_array_mut<'a>(&'a mut self) -> Option<&'a mut Array<S>> {
154 if let Data::Array(arr) = self {
155 Some(arr)
156 } else {
157 None
158 }
159 }
160
161 pub fn as_str_mut<'s>(&'s mut self) -> Option<&'s mut AtprotoStr<S>> {
163 if let Data::String(s) = self {
164 Some(s)
165 } else {
166 None
167 }
168 }
169
170 pub fn as_integer_mut(&mut self) -> Option<&mut i64> {
172 if let Data::Integer(i) = self {
173 Some(i)
174 } else {
175 None
176 }
177 }
178
179 pub fn as_boolean_mut(&mut self) -> Option<&mut bool> {
181 if let Data::Boolean(b) = self {
182 Some(b)
183 } else {
184 None
185 }
186 }
187
188 pub fn as_integer(&self) -> Option<i64> {
190 if let Data::Integer(i) = self {
191 Some(*i)
192 } else {
193 None
194 }
195 }
196
197 pub fn as_boolean(&self) -> Option<bool> {
199 if let Data::Boolean(b) = self {
200 Some(*b)
201 } else {
202 None
203 }
204 }
205
206 pub fn is_null(&self) -> bool {
208 matches!(self, Data::Null)
209 }
210
211 pub fn type_discriminator(&self) -> Option<&str> {
216 self.as_object()?.type_discriminator()
217 }
218
219 pub fn to_dag_cbor(
223 &self,
224 ) -> Result<Vec<u8>, serde_ipld_dagcbor::EncodeError<alloc::collections::TryReserveError>>
225 where
226 S: Serialize,
227 {
228 serde_ipld_dagcbor::to_vec(self)
229 }
230
231 pub fn get_at_path<'s>(&'s self, path: &str) -> Option<&'s Data<S>> {
246 parse_and_traverse_path(self, path)
247 }
248
249 pub fn get_at_path_mut(&mut self, path: &str) -> Option<&mut Data<S>> {
253 parse_and_traverse_path_mut(self, path)
254 }
255
256 pub fn set_at_path(&mut self, path: &str, new_data: Data<S>) -> bool {
260 if let Some(data) = parse_and_traverse_path_mut(self, path) {
261 *data = new_data;
262 true
263 } else {
264 false
265 }
266 }
267
268 pub fn query<'s>(&'s self, pattern: &str) -> QueryResult<'s, S> {
288 query_data(self, pattern)
289 }
290}
291
292impl<S: Bos<str> + AsRef<str>> Data<S> {
293 pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Data<B> {
295 match self {
296 Data::Null => Data::Null,
297 Data::Boolean(b) => Data::Boolean(b),
298 Data::Integer(i) => Data::Integer(i),
299 Data::String(s) => Data::String(s.convert()),
300 Data::Bytes(b) => Data::Bytes(b),
301 Data::CidLink(cid) => Data::CidLink(cid.convert()),
302 Data::Array(arr) => Data::Array(arr.convert()),
303 Data::Object(obj) => Data::Object(obj.convert()),
304 Data::Blob(blob) => Data::Blob(blob.convert()),
305 Data::InvalidNumber(float) => Data::InvalidNumber(float.into()),
306 }
307 }
308}
309
310impl<S> IntoStatic for Data<S>
311where
312 S: Bos<str> + AsRef<str> + IntoStatic,
313 <S as IntoStatic>::Output: AsRef<str> + Bos<str>,
314{
315 type Output = Data<<S as IntoStatic>::Output>;
316 fn into_static(self) -> Data<<S as IntoStatic>::Output> {
317 match self {
318 Data::Null => Data::Null,
319 Data::Boolean(bool) => Data::Boolean(bool),
320 Data::Integer(int) => Data::Integer(int),
321 Data::String(string) => Data::String(string.into_static()),
322 Data::Bytes(bytes) => Data::Bytes(bytes),
323 Data::Array(array) => Data::Array(array.into_static()),
324 Data::Object(object) => Data::Object(object.into_static()),
325 Data::CidLink(cid) => Data::CidLink(cid.into_static()),
326 Data::Blob(blob) => Data::Blob(blob.into_static()),
327 Data::InvalidNumber(float) => Data::InvalidNumber(float.into_static()),
328 }
329 }
330}
331
332#[derive(Debug, Clone, PartialEq, Eq)]
334pub struct Array<S = DefaultStr>(pub Vec<Data<S>>)
335where
336 S: Bos<str> + AsRef<str>;
337
338impl<S: Bos<str> + AsRef<str>> Array<S> {
339 pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Array<B> {
341 Array(self.0.into_iter().map(|d| d.convert()).collect())
342 }
343}
344
345impl<S> IntoStatic for Array<S>
346where
347 S: Bos<str> + AsRef<str> + IntoStatic,
348 <S as IntoStatic>::Output: AsRef<str> + Bos<str>,
349{
350 type Output = Array<<S as IntoStatic>::Output>;
351 fn into_static(self) -> Array<<S as IntoStatic>::Output> {
352 Array(self.0.into_static())
353 }
354}
355
356impl<S> Array<S>
357where
358 S: Bos<str> + AsRef<str>,
359{
360 pub fn len(&self) -> usize {
362 self.0.len()
363 }
364
365 pub fn is_empty(&self) -> bool {
367 self.0.is_empty()
368 }
369
370 pub fn get(&self, index: usize) -> Option<&Data<S>> {
372 self.0.get(index)
373 }
374
375 pub fn get_mut(&mut self, index: usize) -> Option<&mut Data<S>> {
377 self.0.get_mut(index)
378 }
379
380 pub fn iter(&self) -> core::slice::Iter<'_, Data<S>> {
382 self.0.iter()
383 }
384}
385
386impl<S> core::ops::Index<usize> for Array<S>
387where
388 S: AsRef<str> + Bos<str>,
389{
390 type Output = Data<S>;
391
392 fn index(&self, index: usize) -> &Self::Output {
393 &self.0[index]
394 }
395}
396
397#[derive(Debug, Clone, PartialEq, Eq)]
399pub struct Object<S = DefaultStr>(pub BTreeMap<SmolStr, Data<S>>)
400where
401 S: Bos<str> + AsRef<str>;
402
403impl<S: Bos<str> + AsRef<str>> Object<S> {
404 pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Object<B> {
406 Object(self.0.into_iter().map(|(k, v)| (k, v.convert())).collect())
407 }
408}
409
410impl<S> IntoStatic for Object<S>
411where
412 S: Bos<str> + AsRef<str> + IntoStatic,
413 <S as IntoStatic>::Output: AsRef<str> + Bos<str>,
414{
415 type Output = Object<<S as IntoStatic>::Output>;
416 fn into_static(self) -> Object<<S as IntoStatic>::Output> {
417 Object(self.0.into_static())
418 }
419}
420
421impl<S> Object<S>
422where
423 S: AsRef<str> + Bos<str>,
424{
425 pub fn get(&self, key: &str) -> Option<&Data<S>> {
427 self.0.get(key)
428 }
429
430 pub fn get_mut(&mut self, key: &str) -> Option<&mut Data<S>> {
432 self.0.get_mut(key)
433 }
434
435 pub fn contains_key(&self, key: &str) -> bool {
437 self.0.contains_key(key)
438 }
439
440 pub fn len(&self) -> usize {
442 self.0.len()
443 }
444
445 pub fn is_empty(&self) -> bool {
447 self.0.is_empty()
448 }
449
450 pub fn iter(&self) -> alloc::collections::btree_map::Iter<'_, SmolStr, Data<S>> {
452 self.0.iter()
453 }
454
455 pub fn keys(&self) -> alloc::collections::btree_map::Keys<'_, SmolStr, Data<S>> {
457 self.0.keys()
458 }
459
460 pub fn type_discriminator(&self) -> Option<&str> {
464 self.get("$type")?.as_str()
465 }
466
467 pub fn values(&self) -> alloc::collections::btree_map::Values<'_, SmolStr, Data<S>> {
469 self.0.values()
470 }
471}
472
473impl<S> core::ops::Index<&str> for Object<S>
474where
475 S: AsRef<str> + Bos<str>,
476{
477 type Output = Data<S>;
478
479 fn index(&self, key: &str) -> &Self::Output {
480 &self.0[key]
481 }
482}
483
484#[derive(Debug, Clone, PartialEq, Eq)]
490pub enum RawData<'s> {
491 Null,
493 Boolean(bool),
495 SignedInt(i64),
497 UnsignedInt(u64),
499 String(CowStr<'s>),
501 Bytes(Bytes),
503 CidLink(Cid<CowStr<'s>>),
505 Array(Vec<RawData<'s>>),
507 Object(BTreeMap<SmolStr, RawData<'s>>),
509 Blob(Blob<CowStr<'s>>),
511 InvalidBlob(Box<RawData<'s>>),
513 InvalidNumber(Bytes),
515 InvalidData(Bytes),
517}
518
519impl<'d> RawData<'d> {
520 pub fn as_object(&self) -> Option<&BTreeMap<SmolStr, RawData<'d>>> {
522 if let RawData::Object(obj) = self {
523 Some(obj)
524 } else {
525 None
526 }
527 }
528
529 pub fn as_array(&self) -> Option<&Vec<RawData<'d>>> {
531 if let RawData::Array(arr) = self {
532 Some(arr)
533 } else {
534 None
535 }
536 }
537
538 pub fn as_str(&self) -> Option<&str> {
540 if let RawData::String(s) = self {
541 Some(s.as_ref())
542 } else {
543 None
544 }
545 }
546
547 pub fn as_boolean(&self) -> Option<bool> {
549 if let RawData::Boolean(b) = self {
550 Some(*b)
551 } else {
552 None
553 }
554 }
555
556 pub fn as_object_mut(&mut self) -> Option<&mut BTreeMap<SmolStr, RawData<'d>>> {
558 if let RawData::Object(obj) = self {
559 Some(obj)
560 } else {
561 None
562 }
563 }
564
565 pub fn as_array_mut(&mut self) -> Option<&mut Vec<RawData<'d>>> {
567 if let RawData::Array(arr) = self {
568 Some(arr)
569 } else {
570 None
571 }
572 }
573
574 pub fn as_str_mut(&mut self) -> Option<&mut CowStr<'d>> {
576 if let RawData::String(s) = self {
577 Some(s)
578 } else {
579 None
580 }
581 }
582
583 pub fn as_boolean_mut(&mut self) -> Option<&mut bool> {
585 if let RawData::Boolean(b) = self {
586 Some(b)
587 } else {
588 None
589 }
590 }
591
592 pub fn is_null(&self) -> bool {
594 matches!(self, RawData::Null)
595 }
596
597 pub fn type_discriminator(&self) -> Option<&str> {
602 let obj = self.as_object()?;
603 let type_val = obj.get("$type")?;
604 type_val.as_str()
605 }
606
607 pub fn to_dag_cbor(
611 &self,
612 ) -> Result<Vec<u8>, serde_ipld_dagcbor::EncodeError<alloc::collections::TryReserveError>> {
613 serde_ipld_dagcbor::to_vec(self)
614 }
615
616 pub fn get_at_path(&'d self, path: &str) -> Option<&'d RawData<'d>> {
631 parse_and_traverse_raw_path(self, path)
632 }
633
634 pub fn get_at_path_mut<'a>(&'a mut self, path: &str) -> Option<&'a mut RawData<'d>> {
638 parse_and_traverse_raw_path_mut(self, path)
639 }
640
641 pub fn from_cbor(cbor: &'d Ipld) -> Result<Self, AtDataError> {
644 Ok(match cbor {
645 Ipld::Null => RawData::Null,
646 Ipld::Bool(bool) => RawData::Boolean(*bool),
647 Ipld::Integer(int) => {
648 if *int > i64::MAX as i128 {
649 RawData::UnsignedInt(*int as u64)
650 } else {
651 RawData::SignedInt(*int as i64)
652 }
653 }
654 Ipld::Float(_) => {
655 return Err(AtDataError::FloatNotAllowed);
656 }
657 Ipld::String(string) => Self::String(CowStr::Borrowed(&string)),
658 Ipld::Bytes(items) => Self::Bytes(Bytes::copy_from_slice(items.as_slice())),
659 Ipld::List(iplds) => Self::Array(
660 iplds
661 .into_iter()
662 .filter_map(|item| RawData::from_cbor(item).ok())
663 .collect(),
664 ),
665 Ipld::Map(btree_map) => Self::Object(
666 btree_map
667 .into_iter()
668 .filter_map(|(key, value)| {
669 if let Ok(value) = RawData::from_cbor(value) {
670 Some((key.to_smolstr(), value))
671 } else {
672 None
673 }
674 })
675 .collect(),
676 ),
677 Ipld::Link(cid) => Self::CidLink(Cid::ipld(*cid)),
678 })
679 }
680}
681
682impl IntoStatic for RawData<'_> {
683 type Output = RawData<'static>;
684
685 fn into_static(self) -> Self::Output {
686 match self {
687 RawData::Null => RawData::Null,
688 RawData::Boolean(b) => RawData::Boolean(b),
689 RawData::SignedInt(i) => RawData::SignedInt(i),
690 RawData::UnsignedInt(u) => RawData::UnsignedInt(u),
691 RawData::String(s) => RawData::String(s.into_static()),
692 RawData::Bytes(b) => RawData::Bytes(b.into_static()),
693 RawData::CidLink(c) => RawData::CidLink(c.into_static()),
694 RawData::Array(a) => RawData::Array(a.into_static()),
695 RawData::Object(o) => RawData::Object(o.into_static()),
696 RawData::Blob(b) => RawData::Blob(b.into_static()),
697 RawData::InvalidBlob(b) => RawData::InvalidBlob(b.into_static()),
698 RawData::InvalidNumber(b) => RawData::InvalidNumber(b.into_static()),
699 RawData::InvalidData(b) => RawData::InvalidData(b.into_static()),
700 }
701 }
702}
703
704pub fn from_data<'de, T, S>(data: &'de Data<S>) -> Result<T, DataDeserializerError>
730where
731 T: serde::Deserialize<'de>,
732 S: Bos<str> + AsRef<str> + serde::Deserialize<'de> + core::convert::From<&'de str>,
733{
734 T::deserialize(data)
735}
736
737pub fn from_data_owned<'de, T, S>(
741 data: Data<S>,
742) -> Result<<T as IntoStatic>::Output, DataDeserializerError>
743where
744 T: serde::Deserialize<'de> + IntoStatic,
745 S: Bos<str> + AsRef<str> + serde::Deserialize<'de> + IntoStatic + core::convert::From<&'de str>,
746 <S as IntoStatic>::Output: Bos<str> + AsRef<str>,
747{
748 T::deserialize(data).map(|d| d.into_static())
749}
750
751pub fn from_json_value<'de, T>(
755 json: serde_json::Value,
756) -> Result<<T as IntoStatic>::Output, serde_json::Error>
757where
758 T: serde::Deserialize<'de> + IntoStatic,
759{
760 T::deserialize(json).map(IntoStatic::into_static)
761}
762
763pub fn from_cbor<'de, T>(
767 cbor: &'de [u8],
768) -> Result<<T as IntoStatic>::Output, serde_ipld_dagcbor::DecodeError<Infallible>>
769where
770 T: serde::Deserialize<'de> + IntoStatic,
771{
772 serde_ipld_dagcbor::from_slice::<T>(cbor).map(|d| d.into_static())
773}
774
775pub fn from_postcard<'de, T>(bytes: &'de [u8]) -> Result<<T as IntoStatic>::Output, postcard::Error>
779where
780 T: serde::Deserialize<'de> + IntoStatic,
781{
782 postcard::from_bytes::<T>(bytes).map(|d| d.into_static())
783}
784
785pub fn from_raw_data<'de, T>(data: &'de RawData<'de>) -> Result<T, DataDeserializerError>
808where
809 T: serde::Deserialize<'de>,
810{
811 T::deserialize(data)
812}
813
814pub fn from_raw_data_owned<'de, T>(data: RawData<'_>) -> Result<T, DataDeserializerError>
818where
819 T: serde::Deserialize<'de>,
820{
821 T::deserialize(data.into_static())
822}
823
824pub fn to_raw_data<T>(value: &T) -> Result<RawData<'static>, serde_impl::RawDataSerializerError>
846where
847 T: serde::Serialize,
848{
849 value.serialize(serde_impl::RawDataSerializer)
850}
851
852pub fn to_data<'s, T>(value: &T) -> Result<Data<SmolStr>, serde_impl::RawDataSerializerError>
877where
878 T: serde::Serialize,
879{
880 value.serialize(serde_impl::DataSerializer)
881}
882
883fn parse_and_traverse_path<'s, S>(data: &'s Data<S>, path: &str) -> Option<&'s Data<S>>
885where
886 S: AsRef<str> + Bos<str>,
887{
888 let mut current = data;
889 let mut path = path.trim_start_matches('.');
890
891 while !path.is_empty() {
892 if path.starts_with('[') {
893 let idx_end = path.find(']')?;
895 let idx_str = &path[1..idx_end];
896 let idx: usize = idx_str.parse().ok()?;
897
898 current = current.as_array()?.get(idx)?;
899 path = &path[idx_end + 1..].trim_start_matches('.');
900 } else {
901 let next_sep = path.find(&['.', '['][..]).unwrap_or(path.len());
903 let field = &path[..next_sep];
904
905 if field.is_empty() {
906 break;
907 }
908
909 current = current.as_object()?.get(field)?;
910 path = &path[next_sep..].trim_start_matches('.');
911 }
912 }
913
914 Some(current)
915}
916
917fn parse_and_traverse_raw_path<'d>(data: &'d RawData<'d>, path: &str) -> Option<&'d RawData<'d>> {
919 let mut current = data;
920 let mut path = path.trim_start_matches('.');
921
922 while !path.is_empty() {
923 if path.starts_with('[') {
924 let idx_end = path.find(']')?;
926 let idx_str = &path[1..idx_end];
927 let idx: usize = idx_str.parse().ok()?;
928
929 current = current.as_array()?.get(idx)?;
930 path = &path[idx_end + 1..].trim_start_matches('.');
931 } else {
932 let next_sep = path.find(&['.', '['][..]).unwrap_or(path.len());
934 let field = &path[..next_sep];
935
936 if field.is_empty() {
937 break;
938 }
939
940 current = current.as_object()?.get(field as &str)?;
941 path = &path[next_sep..].trim_start_matches('.');
942 }
943 }
944
945 Some(current)
946}
947
948fn parse_and_traverse_path_mut<'d, 's, S>(
950 data: &'s mut Data<S>,
951 path: &str,
952) -> Option<&'s mut Data<S>>
953where
954 S: AsRef<str> + Bos<str>,
955{
956 let mut current = data;
957 let mut path = path.trim_start_matches('.');
958
959 while !path.is_empty() {
960 if path.starts_with('[') {
961 let idx_end = path.find(']')?;
963 let idx_str = &path[1..idx_end];
964 let idx: usize = idx_str.parse().ok()?;
965
966 current = current.as_array_mut()?.get_mut(idx)?;
967 path = &path[idx_end + 1..].trim_start_matches('.');
968 } else {
969 let next_sep = path.find(&['.', '['][..]).unwrap_or(path.len());
971 let field = &path[..next_sep];
972
973 if field.is_empty() {
974 break;
975 }
976
977 current = current.as_object_mut()?.get_mut(field)?;
978 path = &path[next_sep..].trim_start_matches('.');
979 }
980 }
981
982 Some(current)
983}
984
985fn parse_and_traverse_raw_path_mut<'a, 'd>(
987 data: &'a mut RawData<'d>,
988 path: &str,
989) -> Option<&'a mut RawData<'d>> {
990 let mut current = data;
991 let mut path = path.trim_start_matches('.');
992
993 while !path.is_empty() {
994 if path.starts_with('[') {
995 let idx_end = path.find(']')?;
997 let idx_str = &path[1..idx_end];
998 let idx: usize = idx_str.parse().ok()?;
999
1000 current = current.as_array_mut()?.get_mut(idx)?;
1001 path = &path[idx_end + 1..].trim_start_matches('.');
1002 } else {
1003 let next_sep = path.find(&['.', '['][..]).unwrap_or(path.len());
1005 let field = &path[..next_sep];
1006
1007 if field.is_empty() {
1008 break;
1009 }
1010
1011 current = current.as_object_mut()?.get_mut(field as &str)?;
1012 path = &path[next_sep..].trim_start_matches('.');
1013 }
1014 }
1015
1016 Some(current)
1017}
1018
1019#[derive(Debug, Clone, PartialEq)]
1021pub enum QueryResult<'s, S>
1022where
1023 S: AsRef<str> + Bos<str>,
1024{
1025 Single(&'s Data<S>),
1027
1028 Multiple(Vec<QueryMatch<'s, S>>),
1030
1031 None,
1033}
1034
1035impl<'s, S> QueryResult<'s, S>
1036where
1037 S: AsRef<str> + Bos<str>,
1038{
1039 pub fn single(&self) -> Option<&'s Data<S>> {
1041 match self {
1042 QueryResult::Single(data) => Some(data),
1043 _ => None,
1044 }
1045 }
1046
1047 pub fn multiple(&self) -> Option<&[QueryMatch<'s, S>]> {
1049 match self {
1050 QueryResult::Multiple(matches) => Some(matches),
1051 _ => None,
1052 }
1053 }
1054
1055 pub fn first(&self) -> Option<&'s Data<S>> {
1057 match self {
1058 QueryResult::Single(data) => Some(data),
1059 QueryResult::Multiple(matches) => matches.first().and_then(|m| m.value),
1060 QueryResult::None => None,
1061 }
1062 }
1063
1064 pub fn is_empty(&self) -> bool {
1066 matches!(self, QueryResult::None)
1067 }
1068
1069 pub fn values(&self) -> impl Iterator<Item = &'s Data<S>> {
1071 match self {
1072 QueryResult::Single(data) => vec![*data].into_iter(),
1073 QueryResult::Multiple(matches) => matches
1074 .iter()
1075 .filter_map(|m| m.value)
1076 .collect::<Vec<_>>()
1077 .into_iter(),
1078 QueryResult::None => vec![].into_iter(),
1079 }
1080 }
1081}
1082
1083#[derive(Debug, Clone, PartialEq)]
1085pub struct QueryMatch<'s, S>
1086where
1087 S: AsRef<str> + Bos<str>,
1088{
1089 pub path: SmolStr,
1091 pub value: Option<&'s Data<S>>,
1093}
1094
1095#[derive(Debug, Clone, PartialEq)]
1097enum QuerySegment {
1098 Field(SmolStr),
1100 Wildcard,
1102 ScopedRecursion(SmolStr),
1104 GlobalRecursion(SmolStr),
1106}
1107
1108fn parse_query_pattern(pattern: &str) -> Vec<QuerySegment> {
1110 let mut segments = Vec::new();
1111 let mut remaining = pattern;
1112
1113 if remaining.starts_with('.') && !remaining.starts_with("..") {
1115 remaining = &remaining[1..];
1116 }
1117
1118 while !remaining.is_empty() {
1119 if remaining.starts_with("...") {
1120 let rest = &remaining[3..];
1122 let end = rest.find(&['.', '['][..]).unwrap_or(rest.len());
1123 let field = SmolStr::new(&rest[..end]);
1124 segments.push(QuerySegment::GlobalRecursion(field));
1125 remaining = &rest[end..];
1126 if remaining.starts_with('.') && !remaining.starts_with("..") {
1128 remaining = &remaining[1..];
1129 }
1130 } else if remaining.starts_with("..") {
1131 let rest = &remaining[2..];
1133 let end = rest.find(&['.', '['][..]).unwrap_or(rest.len());
1134 let field = SmolStr::new(&rest[..end]);
1135 segments.push(QuerySegment::ScopedRecursion(field));
1136 remaining = &rest[end..];
1137 if remaining.starts_with('.') && !remaining.starts_with("..") {
1139 remaining = &remaining[1..];
1140 }
1141 } else if remaining.starts_with("[..]") {
1142 segments.push(QuerySegment::Wildcard);
1144 remaining = &remaining[4..];
1145 if remaining.starts_with('.') && !remaining.starts_with("..") {
1147 remaining = &remaining[1..];
1148 }
1149 } else {
1150 let end = remaining.find(&['.', '['][..]).unwrap_or(remaining.len());
1152 let field = &remaining[..end];
1153 if !field.is_empty() {
1154 segments.push(QuerySegment::Field(SmolStr::new(field)));
1155 }
1156 remaining = &remaining[end..];
1157 if remaining.starts_with('.') && !remaining.starts_with("..") {
1159 remaining = &remaining[1..];
1160 }
1161 }
1162 }
1163
1164 segments
1165}
1166
1167fn query_data<'s, S>(data: &'s Data<S>, pattern: &str) -> QueryResult<'s, S>
1169where
1170 S: AsRef<str> + Bos<str>,
1171{
1172 let segments = parse_query_pattern(pattern);
1173 if segments.is_empty() {
1174 return QueryResult::None;
1175 }
1176
1177 let mut results = vec![QueryMatch {
1178 path: SmolStr::new_static(""),
1179 value: Some(data),
1180 }];
1181
1182 let has_wildcard = segments.iter().any(|s| matches!(s, QuerySegment::Wildcard));
1184 let has_global = segments
1185 .iter()
1186 .any(|s| matches!(s, QuerySegment::GlobalRecursion(_)));
1187
1188 for segment in segments {
1189 results = execute_segment(&results, &segment);
1190 if results.is_empty() {
1191 return QueryResult::None;
1192 }
1193 }
1194
1195 if has_wildcard || has_global || results.len() > 1 {
1196 QueryResult::Multiple(results)
1197 } else if results.len() == 1 {
1198 if let Some(value) = results[0].value {
1199 QueryResult::Single(value)
1200 } else {
1201 QueryResult::None
1202 }
1203 } else {
1204 QueryResult::None
1205 }
1206}
1207
1208fn execute_segment<'s, S>(
1210 current: &[QueryMatch<'s, S>],
1211 segment: &QuerySegment,
1212) -> Vec<QueryMatch<'s, S>>
1213where
1214 S: AsRef<str> + Bos<str>,
1215{
1216 let mut next = Vec::new();
1217
1218 for qm in current {
1219 let Some(data) = qm.value else { continue };
1220
1221 match segment {
1222 QuerySegment::Field(field) => {
1223 if let Some(obj) = data.as_object() {
1224 if let Some(value) = obj.get(field.as_str()) {
1225 let new_path = append_path(&qm.path, field.as_str());
1226 next.push(QueryMatch {
1227 path: new_path,
1228 value: Some(value),
1229 });
1230 }
1231 }
1232 }
1233
1234 QuerySegment::Wildcard => match data {
1235 Data::Array(arr) => {
1236 for (idx, item) in arr.iter().enumerate() {
1237 let new_path = append_path(&qm.path, &format!("[{}]", idx));
1238 next.push(QueryMatch {
1239 path: new_path,
1240 value: Some(item),
1241 });
1242 }
1243 }
1244 Data::Object(obj) => {
1245 for (key, value) in obj.iter() {
1246 let new_path = append_path(&qm.path, key.as_str());
1247 next.push(QueryMatch {
1248 path: new_path,
1249 value: Some(value),
1250 });
1251 }
1252 }
1253 _ => {}
1254 },
1255
1256 QuerySegment::ScopedRecursion(field) => {
1257 if let Some(found) = find_field_recursive(data, field.as_str(), &qm.path) {
1258 next.push(found);
1259 }
1260 }
1261
1262 QuerySegment::GlobalRecursion(field) => {
1263 find_all_fields_recursive(data, field.as_str(), &qm.path, &mut next);
1264 }
1265 }
1266 }
1267
1268 next
1269}
1270
1271fn find_field_recursive<'s, S>(
1273 data: &'s Data<S>,
1274 field: &str,
1275 base_path: &SmolStr,
1276) -> Option<QueryMatch<'s, S>>
1277where
1278 S: AsRef<str> + Bos<str>,
1279{
1280 match data {
1281 Data::Object(obj) => {
1282 if let Some(value) = obj.get(field) {
1284 let new_path = append_path(base_path, field);
1285 return Some(QueryMatch {
1286 path: new_path,
1287 value: Some(value),
1288 });
1289 }
1290
1291 for (key, value) in obj.iter() {
1293 let new_path = append_path(base_path, key.as_str());
1294 if let Some(found) = find_field_recursive(value, field, &new_path) {
1295 return Some(found);
1296 }
1297 }
1298 }
1299 Data::Array(arr) => {
1300 for (idx, item) in arr.iter().enumerate() {
1301 let new_path = append_path(base_path, &format!("[{}]", idx));
1302 if let Some(found) = find_field_recursive(item, field, &new_path) {
1303 return Some(found);
1304 }
1305 }
1306 }
1307 _ => {}
1308 }
1309
1310 None
1311}
1312
1313fn find_all_fields_recursive<'s, S>(
1315 data: &'s Data<S>,
1316 field: &str,
1317 base_path: &SmolStr,
1318 results: &mut Vec<QueryMatch<'s, S>>,
1319) where
1320 S: AsRef<str> + Bos<str>,
1321{
1322 match data {
1323 Data::Object(obj) => {
1324 if let Some(value) = obj.get(field) {
1326 let new_path = append_path(base_path, field);
1327 results.push(QueryMatch {
1328 path: new_path,
1329 value: Some(value),
1330 });
1331 }
1332
1333 for (key, value) in obj.iter() {
1335 let new_path = append_path(base_path, key.as_str());
1336 find_all_fields_recursive(value, field, &new_path, results);
1337 }
1338 }
1339 Data::Array(arr) => {
1340 for (idx, item) in arr.iter().enumerate() {
1341 let new_path = append_path(base_path, &format!("[{}]", idx));
1342 find_all_fields_recursive(item, field, &new_path, results);
1343 }
1344 }
1345 _ => {}
1346 }
1347}
1348
1349fn append_path(base: &SmolStr, segment: &str) -> SmolStr {
1351 if base.is_empty() {
1352 SmolStr::new(segment)
1353 } else if segment.starts_with('[') {
1354 SmolStr::new(format!("{}{}", base, segment))
1355 } else {
1356 SmolStr::new(format!("{}.{}", base, segment))
1357 }
1358}