nova_forms/
query_string.rs1use std::fmt::Display;
2
3use leptos::*;
4use ustr::Ustr;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub enum QueryStringPart {
10 Index(usize),
11 Key(Ustr),
12}
13
14impl Display for QueryStringPart {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 match self {
17 QueryStringPart::Index(i) => write!(f, "{}", i),
18 QueryStringPart::Key(k) => write!(f, "{}", k),
19 }
20 }
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
27pub struct QueryString {
28 parts: [Option<QueryStringPart>; 16],
29 len: usize,
30}
31
32impl FromIterator<QueryStringPart> for QueryString {
33 fn from_iter<T: IntoIterator<Item = QueryStringPart>>(iter: T) -> Self {
34 let mut qs = QueryString::default();
35 let mut len = 0;
36 for (i, part) in iter.into_iter().enumerate() {
37 qs.parts[i] = Some(part);
38 len += 1;
39 }
40 qs.len = len;
41 qs
42 }
43}
44
45impl<'a> FromIterator<&'a QueryStringPart> for QueryString {
46 fn from_iter<T: IntoIterator<Item = &'a QueryStringPart>>(iter: T) -> Self {
47 let mut qs = QueryString::default();
48 let mut len = 0;
49 for (i, part) in iter.into_iter().enumerate() {
50 qs.parts[i] = Some(*part);
51 len += 1;
52 }
53 qs.len = len;
54 qs
55 }
56}
57
58impl QueryString {
59 pub fn iter(&self) -> impl Iterator<Item = &QueryStringPart> {
60 self.parts.iter().flatten().fuse()
61 }
62
63 pub fn len(&self) -> usize {
64 self.iter().count()
65 }
66
67 pub fn extends(&self, other: &Self) -> Option<QueryString> {
69 if self.len() < other.len() {
70 return None;
71 }
72
73 if !self.iter().zip(other.iter()).all(|(s, o)| s == o) {
74 return None;
75 }
76
77 Some(self.iter().skip(other.len()).collect())
78 }
79
80 pub fn join(self, other: Self) -> Self {
82 self.iter().chain(other.iter()).collect()
83 }
84
85 pub fn add(mut self, part: QueryStringPart) -> Self {
86 self.parts[self.len] = Some(part);
87 self.len += 1;
88 self
89 }
90
91 pub fn add_index(self, index: usize) -> Self {
92 self.add(QueryStringPart::Index(index))
93 }
94
95 pub fn add_key<K: AsRef<str>>(self, key: K) -> Self {
96 self.add(QueryStringPart::Key(Ustr::from(key.as_ref())))
97 }
98}
99
100impl IntoAttribute for QueryString {
101 fn into_attribute(self) -> Attribute {
102 Attribute::String(Oco::Owned(format!("{self}")))
103 }
104
105 fn into_attribute_boxed(self: Box<Self>) -> Attribute {
106 Attribute::String(Oco::Owned(format!("{self}")))
107 }
108}
109
110impl From<&str> for QueryString {
111 fn from(value: &str) -> Self {
112 let mut chars = value.chars();
113 let mut parts = Vec::new();
114 while let Some(c) = chars.next() {
115 match c {
116 '[' => parts.push(String::new()),
117 ']' => {}
118 _ => {
119 if let Some(last) = parts.last_mut() {
120 last.push(c);
121 } else {
122 parts.push(String::from(c));
123 }
124 }
125 }
126 }
127
128 parts
129 .into_iter()
130 .map(|p| {
131 p.parse::<usize>()
132 .map(QueryStringPart::Index)
133 .unwrap_or_else(|_| QueryStringPart::Key(Ustr::from(&p)))
134 })
135 .collect()
136 }
137}
138
139impl From<String> for QueryString {
140 fn from(value: String) -> Self {
141 QueryString::from(value.as_str())
142 }
143}
144
145impl Display for QueryString {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 let mut iter = self.iter();
148 if let Some(first) = iter.next() {
149 write!(f, "{}", first)?;
150 }
151 for part in iter {
152 write!(f, "[{}]", part)?;
153 }
154 Ok(())
155 }
156}
157
158#[macro_export]
160macro_rules! qs {
161 ( $key:ident $($t:tt)* ) => {
162 qs!(@part($crate::QueryString::default().add_key(stringify!($key))) $($t)*)
163 };
164 () => {
165 $crate::QueryString::default()
166 };
167 ( @part($part:expr) [ $index:literal ] $($t:tt)* ) => {
168 qs!(@part($part.add_index($index)) $($t)*)
169 };
170 ( @part($part:expr) [ $key:ident ] $($t:tt)* ) => {
171 qs!(@part($part.add_key(stringify!($key))) $($t)*)
172 };
173 (@part($part:expr) ) => {
174 $part
175 };
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_extends() {
184 assert_eq!(QueryString::from("form_data[a][b]").extends(&QueryString::from("form_data[a]")), Some(QueryString::from("b")));
185 }
186
187 #[test]
188 fn test_join() {
189 assert_eq!(QueryString::from("a").join(QueryString::from("b")), QueryString::from("a[b]"));
190 }
191
192 #[test]
193 fn test_add() {
194 assert_eq!(QueryString::from("a").add_key("b"), QueryString::from("a[b]"));
195 assert_eq!(QueryString::from("a").add_index(0), QueryString::from("a[0]"));
196 }
197
198 #[test]
199 fn test_qs_macro() {
200 let qs = qs!(a[b][c]);
201 assert_eq!(qs, QueryString::from("a[b][c]"));
202 let qs = qs!(a[0][c]);
203 assert_eq!(qs, QueryString::from("a[0][c]"));
204 let qs = qs!(a);
205 assert_eq!(qs, QueryString::from("a"));
206 let qs = qs!();
207 assert_eq!(qs, QueryString::default());
208 }
209}