identity_kit/sd_jwt_vc/
path.rs1use core::fmt;
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(untagged)]
11pub enum PathComponent {
12 Key(String),
14 Index(usize),
16 All,
18}
19
20impl From<&str> for PathComponent {
21 fn from(key: &str) -> Self {
22 Self::Key(key.to_owned())
23 }
24}
25
26impl From<String> for PathComponent {
27 fn from(key: String) -> Self {
28 Self::Key(key)
29 }
30}
31
32impl From<usize> for PathComponent {
33 fn from(index: usize) -> Self {
34 Self::Index(index)
35 }
36}
37
38impl From<()> for PathComponent {
39 fn from((): ()) -> Self {
40 Self::All
41 }
42}
43
44impl fmt::Display for PathComponent {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 Self::Key(key) => write!(f, "{key:?}"),
48 Self::Index(index) => write!(f, "{index}"),
49 Self::All => f.write_str("null"),
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
85#[serde(transparent)]
86pub struct ClaimPath(Vec<PathComponent>);
87
88impl ClaimPath {
89 pub fn new<I, C>(components: I) -> Self
92 where
93 I: IntoIterator<Item = C>,
94 C: Into<PathComponent>,
95 {
96 Self(components.into_iter().map(Into::into).collect())
97 }
98
99 pub fn root() -> Self {
101 Self(Vec::new())
102 }
103
104 pub fn components(&self) -> &[PathComponent] {
106 &self.0
107 }
108
109 pub fn is_empty(&self) -> bool {
111 self.0.is_empty()
112 }
113
114 pub fn is_concrete(&self) -> bool {
116 !self.0.contains(&PathComponent::All)
117 }
118
119 pub fn matches(&self, path: &ClaimPath) -> bool {
121 self.0.len() == path.0.len()
122 && self.0.iter().zip(&path.0).all(|(pattern, component)| {
123 *pattern == PathComponent::All && matches!(component, PathComponent::Index(_))
124 || pattern == component
125 })
126 }
127
128 pub fn is_prefix_of(&self, path: &ClaimPath) -> bool {
131 self.0.len() <= path.0.len()
132 && self.0.iter().zip(&path.0).all(|(pattern, component)| {
133 *pattern == PathComponent::All && matches!(component, PathComponent::Index(_))
134 || pattern == component
135 })
136 }
137
138 #[must_use]
140 pub fn join(&self, component: impl Into<PathComponent>) -> Self {
141 let mut components = self.0.clone();
142 components.push(component.into());
143 Self(components)
144 }
145
146 pub fn select<'a>(&self, root: &'a Value) -> Result<Vec<&'a Value>, ClaimPathError> {
154 let mut selected = vec![root];
155 for (depth, component) in self.0.iter().enumerate() {
156 let mut next = Vec::new();
157 for value in selected {
158 match (component, value) {
159 (PathComponent::Key(key), Value::Object(object)) => {
160 next.extend(object.get(key));
161 }
162 (PathComponent::Index(index), Value::Array(array)) => {
163 next.extend(array.get(*index));
164 }
165 (PathComponent::All, Value::Array(array)) => next.extend(array),
166 (PathComponent::Key(_), _) => {
167 return Err(ClaimPathError {
168 path: self.clone(),
169 depth,
170 expected: "an object",
171 });
172 }
173 _ => {
174 return Err(ClaimPathError {
175 path: self.clone(),
176 depth,
177 expected: "an array",
178 });
179 }
180 }
181 }
182 selected = next;
183 }
184 Ok(selected)
185 }
186
187 pub fn resolve(&self, root: &Value) -> Result<Vec<ClaimPath>, ClaimPathError> {
194 let mut selected = vec![(ClaimPath(Vec::new()), root)];
195 for (depth, component) in self.0.iter().enumerate() {
196 let mut next = Vec::new();
197 for (path, value) in selected {
198 match (component, value) {
199 (PathComponent::Key(key), Value::Object(object)) => {
200 if let Some(value) = object.get(key) {
201 next.push((path.join(key.as_str()), value));
202 }
203 }
204 (PathComponent::Index(index), Value::Array(array)) => {
205 if let Some(value) = array.get(*index) {
206 next.push((path.join(*index), value));
207 }
208 }
209 (PathComponent::All, Value::Array(array)) => {
210 next.extend(
211 array
212 .iter()
213 .enumerate()
214 .map(|(index, value)| (path.join(index), value)),
215 );
216 }
217 (PathComponent::Key(_), _) => {
218 return Err(ClaimPathError {
219 path: self.clone(),
220 depth,
221 expected: "an object",
222 });
223 }
224 _ => {
225 return Err(ClaimPathError {
226 path: self.clone(),
227 depth,
228 expected: "an array",
229 });
230 }
231 }
232 }
233 selected = next;
234 }
235 Ok(selected.into_iter().map(|(path, _)| path).collect())
236 }
237}
238
239impl<C: Into<PathComponent>> FromIterator<C> for ClaimPath {
240 fn from_iter<I: IntoIterator<Item = C>>(iter: I) -> Self {
241 Self::new(iter)
242 }
243}
244
245impl<C: Into<PathComponent>, const N: usize> From<[C; N]> for ClaimPath {
246 fn from(components: [C; N]) -> Self {
247 Self::new(components)
248 }
249}
250
251impl From<Vec<PathComponent>> for ClaimPath {
252 fn from(components: Vec<PathComponent>) -> Self {
253 Self(components)
254 }
255}
256
257impl fmt::Display for ClaimPath {
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 f.write_str("[")?;
260 for (i, component) in self.0.iter().enumerate() {
261 if i > 0 {
262 f.write_str(", ")?;
263 }
264 write!(f, "{component}")?;
265 }
266 f.write_str("]")
267 }
268}
269
270#[macro_export]
287macro_rules! claim_path {
288 ($($component:expr),* $(,)?) => {
289 $crate::sd_jwt_vc::ClaimPath::from(vec![$($crate::sd_jwt_vc::PathComponent::from($component)),*])
290 };
291}
292
293#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
295#[error("claim path {path}: component {depth} expects {expected}")]
296pub struct ClaimPathError {
297 pub path: ClaimPath,
299 pub depth: usize,
301 pub expected: &'static str,
303}