1use super::{
18 err::{JsonDeserializationError, JsonDeserializationErrorContext, JsonSerializationError},
19 SchemaType,
20};
21use crate::entities::{
22 conformance::err::EntitySchemaConformanceError,
23 json::err::{EscapeKind, TypeMismatchError},
24};
25use crate::extensions::Extensions;
26use crate::FromNormalizedStr;
27use crate::{
28 ast::{
29 expression_construction_errors, BorrowedRestrictedExpr, Eid, EntityUID, ExprKind,
30 ExpressionConstructionError, Literal, RestrictedExpr, Unknown, Value, ValueKind,
31 },
32 entities::Name,
33};
34use either::Either;
35use itertools::Itertools;
36use serde::{Deserialize, Serialize};
37use serde_with::serde_as;
38use serde_with::{DeserializeAs, SerializeAs};
39use smol_str::{SmolStr, ToSmolStr};
40use std::collections::{BTreeMap, HashSet};
41use std::sync::Arc;
42
43#[cfg(feature = "wasm")]
44extern crate tsify;
45
46#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
47#[serde(untagged)]
48enum RawCedarValueJson {
49 Bool(bool),
51 Long(i64),
53 String(SmolStr),
55 Set(Vec<RawCedarValueJson>),
58 Record(RawJsonRecord),
61 Null,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
75#[serde(untagged)]
76#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
77#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
78pub enum CedarValueJson {
79 ExprEscape {
81 #[cfg_attr(feature = "wasm", tsify(type = "__skip"))]
83 __expr: SmolStr,
84 },
85 EntityEscape {
94 __entity: TypeAndId,
96 },
97 ExtnEscape {
106 __extn: FnAndArgs,
108 },
109 Bool(bool),
111 Long(i64),
113 String(#[cfg_attr(feature = "wasm", tsify(type = "string"))] SmolStr),
115 Set(Vec<CedarValueJson>),
118 Record(
121 #[cfg_attr(feature = "wasm", tsify(type = "{ [key: string]: CedarValueJson }"))] JsonRecord,
122 ),
123 Null,
126}
127
128impl<'de> Deserialize<'de> for CedarValueJson {
129 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130 where
131 D: serde::Deserializer<'de>,
132 {
133 let v: RawCedarValueJson = RawCedarValueJson::deserialize(deserializer)?;
134 Ok(v.into())
135 }
136}
137
138impl From<RawCedarValueJson> for CedarValueJson {
139 fn from(value: RawCedarValueJson) -> Self {
140 match value {
141 RawCedarValueJson::Bool(b) => Self::Bool(b),
142 RawCedarValueJson::Long(l) => Self::Long(l),
143 RawCedarValueJson::Null => Self::Null,
144 RawCedarValueJson::Record(r) => {
145 let values = &r.values;
146 if values.len() == 1 {
147 match values.iter().map(|(k, v)| (k.as_str(), v)).collect_vec()[..] {
148 [("__extn", RawCedarValueJson::Record(r))] if r.values.len() >= 2 => {
149 if let Some(RawCedarValueJson::String(fn_name)) = r.values.get("fn") {
150 if let Some(arg) = r.values.get("arg") {
151 return Self::ExtnEscape {
152 __extn: FnAndArgs::Single {
153 ext_fn: fn_name.clone(),
154 arg: Box::new(arg.clone().into()),
155 },
156 };
157 }
158 if let Some(RawCedarValueJson::Set(args)) = r.values.get("args") {
159 return Self::ExtnEscape {
160 __extn: FnAndArgs::Multi {
161 ext_fn: fn_name.clone(),
162 args: args.iter().cloned().map(Into::into).collect(),
163 },
164 };
165 }
166 }
167 }
168 [("__expr", RawCedarValueJson::String(s))] => {
169 return Self::ExprEscape { __expr: s.clone() };
170 }
171 [("__entity", RawCedarValueJson::Record(r))] if r.values.len() >= 2 => {
172 if let Some(RawCedarValueJson::String(ty)) = r.values.get("type") {
173 if let Some(RawCedarValueJson::String(id)) = r.values.get("id") {
174 return Self::EntityEscape {
175 __entity: TypeAndId {
176 entity_type: ty.clone(),
177 id: id.clone(),
178 },
179 };
180 }
181 }
182 }
183 _ => {}
184 }
185 }
186 Self::Record(r.into())
187 }
188 RawCedarValueJson::Set(s) => Self::Set(s.into_iter().map(Into::into).collect()),
189 RawCedarValueJson::String(s) => Self::String(s),
190 }
191 }
192}
193
194#[serde_as]
195#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
196struct RawJsonRecord {
197 #[serde_as(as = "serde_with::MapPreventDuplicates<_, _>")]
200 #[serde(flatten)]
201 values: BTreeMap<SmolStr, RawCedarValueJson>,
202}
203
204impl From<RawJsonRecord> for JsonRecord {
205 fn from(value: RawJsonRecord) -> Self {
206 JsonRecord {
207 values: value
208 .values
209 .into_iter()
210 .map(|(k, v)| (k, v.into()))
211 .collect(),
212 }
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
218pub struct JsonRecord {
219 #[serde(flatten)]
222 values: BTreeMap<SmolStr, CedarValueJson>,
223}
224
225impl IntoIterator for JsonRecord {
226 type Item = (SmolStr, CedarValueJson);
227 type IntoIter = <BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
228 fn into_iter(self) -> Self::IntoIter {
229 self.values.into_iter()
230 }
231}
232
233impl<'a> IntoIterator for &'a JsonRecord {
234 type Item = (&'a SmolStr, &'a CedarValueJson);
235 type IntoIter = <&'a BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
236 fn into_iter(self) -> Self::IntoIter {
237 self.values.iter()
238 }
239}
240
241impl FromIterator<(SmolStr, CedarValueJson)> for JsonRecord {
248 fn from_iter<T: IntoIterator<Item = (SmolStr, CedarValueJson)>>(iter: T) -> Self {
249 Self {
250 values: BTreeMap::from_iter(iter),
251 }
252 }
253}
254
255impl JsonRecord {
256 pub fn iter(&self) -> impl Iterator<Item = (&'_ SmolStr, &'_ CedarValueJson)> {
258 self.values.iter()
259 }
260
261 pub fn len(&self) -> usize {
263 self.values.len()
264 }
265
266 pub fn is_empty(&self) -> bool {
268 self.values.is_empty()
269 }
270}
271
272#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
274#[serde(rename_all = "camelCase")]
275#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
276#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
277pub struct TypeAndId {
278 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
280 #[serde(rename = "type")]
281 entity_type: SmolStr,
282 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
284 id: SmolStr,
285}
286
287impl From<EntityUID> for TypeAndId {
288 fn from(euid: EntityUID) -> TypeAndId {
289 let (entity_type, eid) = euid.components();
290 TypeAndId {
291 entity_type: entity_type.to_smolstr(),
292 id: AsRef::<str>::as_ref(&eid).into(),
293 }
294 }
295}
296
297impl From<&EntityUID> for TypeAndId {
298 fn from(euid: &EntityUID) -> TypeAndId {
299 TypeAndId {
300 entity_type: euid.entity_type().to_smolstr(),
301 id: AsRef::<str>::as_ref(&euid.eid()).into(),
302 }
303 }
304}
305
306impl TryFrom<TypeAndId> for EntityUID {
307 type Error = crate::parser::err::ParseErrors;
308
309 fn try_from(e: TypeAndId) -> Result<EntityUID, crate::parser::err::ParseErrors> {
310 Ok(EntityUID::from_components(
311 Name::from_normalized_str(&e.entity_type)?.into(),
312 Eid::new(e.id),
313 None,
314 ))
315 }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
320#[serde(rename_all = "camelCase")]
321#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
322#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
323#[serde(untagged)]
324pub enum FnAndArgs {
325 Single {
327 #[serde(rename = "fn")]
329 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
330 ext_fn: SmolStr,
331 arg: Box<CedarValueJson>,
333 },
334 Multi {
336 #[serde(rename = "fn")]
338 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
339 ext_fn: SmolStr,
340 args: Vec<CedarValueJson>,
342 },
343}
344
345impl FnAndArgs {
346 pub(crate) fn fn_str(&self) -> &str {
347 match self {
348 Self::Multi { ext_fn, .. } | Self::Single { ext_fn, .. } => ext_fn,
349 }
350 }
351
352 pub(crate) fn args(&self) -> &[CedarValueJson] {
353 match self {
354 Self::Multi { args, .. } => args,
355 Self::Single { arg, .. } => std::slice::from_ref(arg),
356 }
357 }
358}
359
360impl CedarValueJson {
361 pub fn uid(euid: &EntityUID) -> Self {
363 Self::EntityEscape {
364 __entity: TypeAndId::from(euid.clone()),
365 }
366 }
367
368 pub fn height(&self) -> usize {
382 let mut stack: Vec<(&CedarValueJson, usize)> = vec![(self, 0)];
385 let mut max_depth: usize = 0;
386 while let Some((val, depth)) = stack.pop() {
387 max_depth = max_depth.max(depth);
388 let child_depth = depth + 1;
389 match val {
390 Self::Bool(_)
391 | Self::Long(_)
392 | Self::String(_)
393 | Self::Null
394 | Self::ExprEscape { .. }
395 | Self::EntityEscape { .. } => {}
396 Self::Set(vals) => {
397 for v in vals {
398 stack.push((v, child_depth));
399 }
400 }
401 Self::Record(rec) => {
402 for (_, v) in rec {
403 stack.push((v, child_depth));
404 }
405 }
406 Self::ExtnEscape { __extn } => {
407 for arg in __extn.args() {
408 stack.push((arg, child_depth));
409 }
410 }
411 }
412 }
413 max_depth
414 }
415
416 pub fn into_expr(
418 self,
419 ctx: &dyn Fn() -> JsonDeserializationErrorContext,
420 ) -> Result<RestrictedExpr, JsonDeserializationError> {
421 match self {
422 Self::Bool(b) => Ok(RestrictedExpr::val(b)),
423 Self::Long(i) => Ok(RestrictedExpr::val(i)),
424 Self::String(s) => Ok(RestrictedExpr::val(s)),
425 Self::Set(vals) => Ok(RestrictedExpr::set(
426 vals.into_iter()
427 .map(|v| v.into_expr(ctx))
428 .collect::<Result<Vec<_>, _>>()?,
429 )),
430 Self::Record(map) => Ok(RestrictedExpr::record(
431 map.into_iter()
432 .map(|(k, v)| Ok((k, v.into_expr(ctx)?)))
433 .collect::<Result<Vec<_>, JsonDeserializationError>>()?,
434 )
435 .map_err(|e| match e {
436 ExpressionConstructionError::DuplicateKey(
437 expression_construction_errors::DuplicateKeyError { key, .. },
438 ) => JsonDeserializationError::duplicate_key(ctx(), key),
439 })?),
440 Self::EntityEscape { __entity: entity } => Ok(RestrictedExpr::val(
441 EntityUID::try_from(entity.clone()).map_err(|errs| {
442 let err_msg = serde_json::to_string_pretty(&entity)
443 .unwrap_or_else(|_| format!("{:?}", &entity));
444 JsonDeserializationError::parse_escape(EscapeKind::Entity, err_msg, errs)
445 })?,
446 )),
447 Self::ExtnEscape { __extn: extn } => extn.into_expr(ctx),
448 Self::ExprEscape { .. } => Err(JsonDeserializationError::ExprTag(Box::new(ctx()))),
449 Self::Null => Err(JsonDeserializationError::Null(Box::new(ctx()))),
450 }
451 }
452
453 pub fn from_expr(expr: BorrowedRestrictedExpr<'_>) -> Result<Self, JsonSerializationError> {
455 match expr.as_ref().expr_kind() {
456 ExprKind::Lit(lit) => Ok(Self::from_lit(lit.clone())),
457 ExprKind::ExtensionFunctionApp { fn_name, args } => match args.as_slice() {
458 [] => Err(JsonSerializationError::call_0_args(fn_name.clone())),
459 [arg] => Ok(Self::ExtnEscape {
460 __extn: FnAndArgs::Single {
461 ext_fn: fn_name.to_smolstr(),
462 arg: Box::new(CedarValueJson::from_expr(
463 BorrowedRestrictedExpr::new_unchecked(arg),
465 )?),
466 },
467 }),
468 args => Ok(Self::ExtnEscape {
469 __extn: FnAndArgs::Multi {
470 ext_fn: fn_name.to_smolstr(),
471 args: args
472 .iter()
473 .map(|arg| {
474 CedarValueJson::from_expr(BorrowedRestrictedExpr::new_unchecked(
475 arg,
476 ))
477 })
478 .collect::<Result<Vec<_>, _>>()?,
479 },
480 }),
481 },
482 ExprKind::Set(exprs) => Ok(Self::Set(
483 exprs
484 .iter()
485 .map(BorrowedRestrictedExpr::new_unchecked) .map(CedarValueJson::from_expr)
487 .collect::<Result<_, JsonSerializationError>>()?,
488 )),
489 ExprKind::Record(map) => {
490 check_for_reserved_keys(map.keys())?;
494 Ok(Self::Record(
495 map.iter()
496 .map(|(k, v)| {
497 Ok((
498 k.clone(),
499 CedarValueJson::from_expr(
500 BorrowedRestrictedExpr::new_unchecked(v),
502 )?,
503 ))
504 })
505 .collect::<Result<_, JsonSerializationError>>()?,
506 ))
507 }
508 ExprKind::Unknown(unknown) => Ok(Self::ExtnEscape {
511 __extn: FnAndArgs::Single {
512 ext_fn: "unknown".into(),
513 arg: Box::new(CedarValueJson::String(unknown.name.clone())),
514 },
515 }),
516 kind => Err(JsonSerializationError::unexpected_restricted_expr_kind(
517 kind.clone(),
518 )),
519 }
520 }
521
522 pub fn from_value(value: Value) -> Result<Self, JsonSerializationError> {
533 Self::from_valuekind(value.value)
534 }
535
536 pub fn from_valuekind(value: ValueKind) -> Result<Self, JsonSerializationError> {
540 match value {
541 ValueKind::Lit(lit) => Ok(Self::from_lit(lit)),
542 ValueKind::Set(set) => Ok(Self::Set(
543 set.iter()
544 .cloned()
545 .map(Self::from_value)
546 .collect::<Result<_, _>>()?,
547 )),
548 ValueKind::Record(record) => {
549 check_for_reserved_keys(record.keys())?;
553 Ok(Self::Record(
554 record
555 .iter()
556 .map(|(k, v)| Ok((k.clone(), Self::from_value(v.clone())?)))
557 .collect::<Result<JsonRecord, JsonSerializationError>>()?,
558 ))
559 }
560 ValueKind::ExtensionValue(ev) => {
561 let ext_func = &ev.func;
562 match ev.args.as_slice() {
563 [] => Err(JsonSerializationError::call_0_args(ext_func.clone())),
564 [ref expr] => Ok(Self::ExtnEscape {
565 __extn: FnAndArgs::Single {
566 ext_fn: ext_func.to_smolstr(),
567 arg: Box::new(Self::from_expr(expr.as_borrowed())?),
568 },
569 }),
570 exprs => Ok(Self::ExtnEscape {
571 __extn: FnAndArgs::Multi {
572 ext_fn: ext_func.to_smolstr(),
573 args: exprs
574 .iter()
575 .map(|expr| Self::from_expr(expr.as_borrowed()))
576 .collect::<Result<Vec<_>, _>>()?,
577 },
578 }),
579 }
580 }
581 }
582 }
583
584 pub fn from_lit(lit: Literal) -> Self {
586 match lit {
587 Literal::Bool(b) => Self::Bool(b),
588 Literal::Long(i) => Self::Long(i),
589 Literal::String(s) => Self::String(s),
590 Literal::EntityUID(euid) => Self::EntityEscape {
591 __entity: Arc::unwrap_or_clone(euid).into(),
592 },
593 }
594 }
595
596 pub fn sub_entity_literals(
598 self,
599 mapping: &BTreeMap<EntityUID, EntityUID>,
600 ) -> Result<Self, JsonDeserializationError> {
601 match self {
602 CedarValueJson::ExprEscape { __expr } => Err(JsonDeserializationError::ExprTag(
604 Box::new(JsonDeserializationErrorContext::Unknown),
605 )),
606 CedarValueJson::EntityEscape { __entity } => {
607 let euid = EntityUID::try_from(__entity.clone());
608 match euid {
609 Ok(euid) => match mapping.get(&euid) {
610 Some(new_euid) => Ok(CedarValueJson::EntityEscape {
611 __entity: new_euid.into(),
612 }),
613 None => Ok(CedarValueJson::EntityEscape { __entity }),
614 },
615 Err(_) => Ok(CedarValueJson::EntityEscape { __entity }),
616 }
617 }
618 CedarValueJson::ExtnEscape {
619 __extn: FnAndArgs::Single { ext_fn, arg },
620 } => Ok(CedarValueJson::ExtnEscape {
621 __extn: FnAndArgs::Single {
622 ext_fn,
623 arg: Box::new((*arg).sub_entity_literals(mapping)?),
624 },
625 }),
626 CedarValueJson::ExtnEscape {
627 __extn: FnAndArgs::Multi { ext_fn, args },
628 } => Ok(CedarValueJson::ExtnEscape {
629 __extn: FnAndArgs::Multi {
630 ext_fn,
631 args: args
632 .into_iter()
633 .map(|arg| arg.sub_entity_literals(mapping))
634 .collect::<Result<Vec<_>, _>>()?,
635 },
636 }),
637 v @ CedarValueJson::Bool(_) => Ok(v),
638 v @ CedarValueJson::Long(_) => Ok(v),
639 v @ CedarValueJson::String(_) => Ok(v),
640 CedarValueJson::Set(v) => Ok(CedarValueJson::Set(
641 v.into_iter()
642 .map(|e| e.sub_entity_literals(mapping))
643 .collect::<Result<Vec<_>, _>>()?,
644 )),
645 CedarValueJson::Record(r) => {
646 let mut new_m = BTreeMap::new();
647 for (k, v) in r.values {
648 new_m.insert(k, v.sub_entity_literals(mapping)?);
649 }
650 Ok(CedarValueJson::Record(JsonRecord { values: new_m }))
651 }
652 v @ CedarValueJson::Null => Ok(v),
653 }
654 }
655}
656
657fn check_for_reserved_keys<'a>(
660 mut keys: impl Iterator<Item = &'a SmolStr>,
661) -> Result<(), JsonSerializationError> {
662 let reserved_keys: HashSet<&str> = HashSet::from_iter(["__entity", "__extn", "__expr"]);
667 let collision = keys.find(|k| reserved_keys.contains(k.as_str()));
668 match collision {
669 Some(collision) => Err(JsonSerializationError::reserved_key(collision.clone())),
670 None => Ok(()),
671 }
672}
673
674impl FnAndArgs {
675 pub fn into_expr(
677 self,
678 ctx: &dyn Fn() -> JsonDeserializationErrorContext,
679 ) -> Result<RestrictedExpr, JsonDeserializationError> {
680 let ext_fn = self.fn_str();
681 let args = self.args();
682 Ok(RestrictedExpr::call_extension_fn(
683 Name::from_normalized_str(ext_fn).map_err(|errs| {
684 JsonDeserializationError::parse_escape(EscapeKind::Extension, ext_fn, errs)
685 })?,
686 args.iter()
687 .map(|arg| CedarValueJson::into_expr(arg.clone(), ctx))
688 .collect::<Result<Vec<_>, _>>()?,
689 ))
690 }
691}
692
693#[derive(Debug, Clone)]
695pub struct ValueParser<'e> {
696 extensions: &'e Extensions<'e>,
698}
699
700impl<'e> ValueParser<'e> {
701 pub fn new(extensions: &'e Extensions<'e>) -> Self {
703 Self { extensions }
704 }
705
706 pub fn val_into_restricted_expr(
711 &self,
712 val: serde_json::Value,
713 expected_ty: Option<&SchemaType>,
714 ctx: &dyn Fn() -> JsonDeserializationErrorContext,
715 ) -> Result<RestrictedExpr, JsonDeserializationError> {
716 let parse_as_unknown = |val: serde_json::Value| {
719 let extjson: ExtnValueJson = serde_json::from_value(val).ok()?;
720 match extjson {
721 ExtnValueJson::ExplicitExtnEscape {
722 __extn: FnAndArgs::Single { ext_fn, arg },
723 } if ext_fn == "unknown" => {
724 let arg = arg.into_expr(ctx).ok()?;
725 let name = arg.as_string()?;
726 Some(RestrictedExpr::unknown(Unknown::new_untyped(name.clone())))
727 }
728 _ => None, }
730 };
731 if let Some(rexpr) = parse_as_unknown(val.clone()) {
732 return Ok(rexpr);
733 }
734 match expected_ty {
736 Some(SchemaType::Entity { .. }) => {
741 let uidjson: EntityUidJson = serde_json::from_value(val)?;
742 Ok(RestrictedExpr::val(uidjson.into_euid(ctx)?))
743 }
744 Some(SchemaType::Extension { ref name, .. }) => {
749 let extjson: ExtnValueJson = serde_json::from_value(val)?;
750 match extjson {
751 ExtnValueJson::ExplicitExprEscape { .. } => {
752 Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
753 }
754 ExtnValueJson::ExplicitExtnEscape { __extn }
755 | ExtnValueJson::ImplicitExtnEscape(__extn) => {
756 let func = self.extensions.func(
757 &Name::from_normalized_str(__extn.fn_str()).map_err(|errs| {
758 JsonDeserializationError::parse_escape(
759 EscapeKind::Extension,
760 __extn.fn_str(),
761 errs,
762 )
763 })?,
764 )?;
765 let arg_types = func.arg_types();
766 let args = __extn.args();
767 if args.len() != arg_types.len() {
768 return Err(JsonDeserializationError::incorrect_num_of_arguments(
769 arg_types.len(),
770 args.len(),
771 __extn.fn_str(),
772 ));
773 }
774 Ok(RestrictedExpr::call_extension_fn(
775 func.name().clone(),
776 arg_types
777 .iter()
778 .zip(args.iter())
779 .map(|(arg_type, arg)| {
780 self.val_into_restricted_expr(
786 serde_json::to_value(arg)?,
789 Some(arg_type),
790 ctx,
791 )
792 })
793 .collect::<Result<Vec<_>, _>>()?,
794 ))
795 }
796 ExtnValueJson::ImplicitConstructor(val) => {
797 let expected_return_type = SchemaType::Extension { name: name.clone() };
798 if let Some(constructor) = self
804 .extensions
805 .lookup_single_arg_constructor(&expected_return_type)
806 {
807 #[expect(
808 clippy::indexing_slicing,
809 reason = "we've concluded above that it has one arugment"
810 )]
811 Ok(RestrictedExpr::call_extension_fn(
812 constructor.name().clone(),
813 std::iter::once(self.val_into_restricted_expr(
814 serde_json::to_value(val)?,
815 Some(&constructor.arg_types()[0]),
816 ctx,
817 )?),
818 ))
819 } else {
820 Err(JsonDeserializationError::missing_implied_constructor(
821 ctx(),
822 expected_return_type,
823 ))
824 }
825 }
826 }
827 }
828 Some(expected_ty @ SchemaType::Set { element_ty }) => match val {
831 serde_json::Value::Array(elements) => Ok(RestrictedExpr::set(
832 elements
833 .into_iter()
834 .map(|element| {
835 self.val_into_restricted_expr(element, Some(element_ty), ctx)
836 })
837 .collect::<Result<Vec<RestrictedExpr>, JsonDeserializationError>>()?,
838 )),
839 val => {
840 let actual_val = {
841 let jvalue: CedarValueJson = serde_json::from_value(val)?;
842 jvalue.into_expr(ctx)?
843 };
844 let err = TypeMismatchError::type_mismatch(
845 expected_ty.clone(),
846 actual_val.try_type_of(self.extensions),
847 actual_val,
848 );
849 match ctx() {
850 JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
851 Err(JsonDeserializationError::EntitySchemaConformance(
852 EntitySchemaConformanceError::type_mismatch(
853 uid,
854 attr,
855 crate::entities::conformance::err::AttrOrTag::Attr,
856 err,
857 ),
858 ))
859 }
860 ctx => Err(JsonDeserializationError::type_mismatch(ctx, err)),
861 }
862 }
863 },
864 Some(
868 expected_ty @ SchemaType::Record {
869 attrs: expected_attrs,
870 open_attrs,
871 },
872 ) => match val {
873 serde_json::Value::Object(mut actual_attrs) => {
874 let mut_actual_attrs = &mut actual_attrs; let rexpr_pairs = expected_attrs
876 .iter()
877 .filter_map(move |(k, expected_attr_ty)| {
878 match mut_actual_attrs.remove(k.as_str()) {
879 Some(actual_attr) => {
880 match self.val_into_restricted_expr(actual_attr, Some(expected_attr_ty.schema_type()), ctx) {
881 Ok(actual_attr) => Some(Ok((k.clone(), actual_attr))),
882 Err(e) => Some(Err(e)),
883 }
884 }
885 None if expected_attr_ty.is_required() => Some(Err(JsonDeserializationError::missing_required_record_attr(ctx(), k.clone()))),
886 None => None,
887 }
888 })
889 .collect::<Result<Vec<(SmolStr, RestrictedExpr)>, JsonDeserializationError>>()?;
890
891 if !open_attrs {
892 if let Some((record_attr, _)) = actual_attrs.into_iter().next() {
895 return Err(JsonDeserializationError::unexpected_record_attr(
896 ctx(),
897 record_attr,
898 ));
899 }
900 }
901
902 RestrictedExpr::record(rexpr_pairs).map_err(|e| match e {
907 ExpressionConstructionError::DuplicateKey(
908 expression_construction_errors::DuplicateKeyError { key, .. },
909 ) => JsonDeserializationError::duplicate_key(ctx(), key),
910 })
911 }
912 val => {
913 let actual_val = {
914 let jvalue: CedarValueJson = serde_json::from_value(val)?;
915 jvalue.into_expr(ctx)?
916 };
917 let err = TypeMismatchError::type_mismatch(
918 expected_ty.clone(),
919 actual_val.try_type_of(self.extensions),
920 actual_val,
921 );
922 match ctx() {
923 JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
924 Err(JsonDeserializationError::EntitySchemaConformance(
925 EntitySchemaConformanceError::type_mismatch(
926 uid,
927 attr,
928 crate::entities::conformance::err::AttrOrTag::Attr,
929 err,
930 ),
931 ))
932 }
933 ctx => Err(JsonDeserializationError::type_mismatch(ctx, err)),
934 }
935 }
936 },
937 Some(_) | None => {
940 let jvalue: CedarValueJson = serde_json::from_value(val)?;
943 Ok(jvalue.into_expr(ctx)?)
944 }
945 }
946 }
947}
948
949pub trait DeserializationContext {
953 fn static_context() -> Option<JsonDeserializationErrorContext>;
956}
957
958#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
961pub struct NoStaticContext;
962
963impl DeserializationContext for NoStaticContext {
964 fn static_context() -> Option<JsonDeserializationErrorContext> {
965 None
966 }
967}
968
969#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
972#[serde(untagged)]
973#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
974#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
975pub enum EntityUidJson<Context = NoStaticContext> {
976 ExplicitExprEscape {
978 #[cfg_attr(feature = "wasm", tsify(type = "__skip"))]
980 __expr: String,
981 #[serde(skip)]
983 context: std::marker::PhantomData<Context>,
984 },
985 ExplicitEntityEscape {
987 __entity: TypeAndId,
989 },
990 ImplicitEntityEscape(TypeAndId),
993
994 FoundValue(#[cfg_attr(feature = "wasm", tsify(type = "__skip"))] serde_json::Value),
996}
997
998impl<'de, C: DeserializationContext> DeserializeAs<'de, EntityUID> for EntityUidJson<C> {
999 fn deserialize_as<D>(deserializer: D) -> Result<EntityUID, D::Error>
1000 where
1001 D: serde::Deserializer<'de>,
1002 {
1003 use serde::de::Error;
1004 let context = || JsonDeserializationErrorContext::Unknown;
1006 let s = EntityUidJson::<C>::deserialize(deserializer)?;
1007 let euid = s.into_euid(&context).map_err(Error::custom)?;
1008 Ok(euid)
1009 }
1010}
1011
1012impl<C> SerializeAs<EntityUID> for EntityUidJson<C> {
1013 fn serialize_as<S>(source: &EntityUID, serializer: S) -> Result<S::Ok, S::Error>
1014 where
1015 S: serde::Serializer,
1016 {
1017 let json: EntityUidJson = source.clone().into();
1018 json.serialize(serializer)
1019 }
1020}
1021
1022impl<C: DeserializationContext> EntityUidJson<C> {
1023 pub fn new(entity_type: impl Into<SmolStr>, id: impl Into<SmolStr>) -> Self {
1027 Self::ImplicitEntityEscape(TypeAndId {
1028 entity_type: entity_type.into(),
1029 id: id.into(),
1030 })
1031 }
1032
1033 pub fn into_euid(
1035 self,
1036 dynamic_ctx: &dyn Fn() -> JsonDeserializationErrorContext,
1037 ) -> Result<EntityUID, JsonDeserializationError> {
1038 let ctx = &|| C::static_context().unwrap_or_else(&dynamic_ctx);
1039 match self {
1040 Self::ExplicitEntityEscape { __entity } | Self::ImplicitEntityEscape(__entity) => {
1041 let jvalue = CedarValueJson::EntityEscape { __entity };
1043 let expr = jvalue.into_expr(ctx)?;
1044 match expr.expr_kind() {
1045 ExprKind::Lit(Literal::EntityUID(euid)) => Ok((**euid).clone()),
1046 _ => Err(JsonDeserializationError::expected_entity_ref(
1047 ctx(),
1048 Either::Right(expr.clone().into()),
1049 )),
1050 }
1051 }
1052 Self::FoundValue(v) => Err(JsonDeserializationError::expected_entity_ref(
1053 ctx(),
1054 Either::Left(v),
1055 )),
1056 Self::ExplicitExprEscape { __expr, .. } => {
1057 Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
1058 }
1059 }
1060 }
1061}
1062
1063impl From<EntityUID> for EntityUidJson {
1065 fn from(uid: EntityUID) -> EntityUidJson {
1066 EntityUidJson::ExplicitEntityEscape {
1067 __entity: uid.into(),
1068 }
1069 }
1070}
1071
1072impl From<&EntityUID> for EntityUidJson {
1074 fn from(uid: &EntityUID) -> EntityUidJson {
1075 EntityUidJson::ExplicitEntityEscape {
1076 __entity: uid.into(),
1077 }
1078 }
1079}
1080
1081#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1084#[serde(untagged)]
1085pub enum ExtnValueJson {
1086 ExplicitExprEscape {
1088 __expr: String,
1090 },
1091 ExplicitExtnEscape {
1093 __extn: FnAndArgs,
1095 },
1096 ImplicitExtnEscape(FnAndArgs),
1099 ImplicitConstructor(CedarValueJson),
1105}
1106
1107#[cfg(test)]
1108mod test {
1109 use super::*;
1110
1111 #[test]
1112 fn height_leaf() {
1113 assert_eq!(CedarValueJson::Bool(true).height(), 0);
1114 assert_eq!(CedarValueJson::Long(42).height(), 0);
1115 assert_eq!(CedarValueJson::String("hi".into()).height(), 0);
1116 assert_eq!(CedarValueJson::Null.height(), 0);
1117 assert_eq!(
1118 CedarValueJson::ExprEscape {
1119 __expr: "1 + 2".into()
1120 }
1121 .height(),
1122 0
1123 );
1124 assert_eq!(
1125 CedarValueJson::EntityEscape {
1126 __entity: TypeAndId {
1127 entity_type: "User".into(),
1128 id: "alice".into(),
1129 }
1130 }
1131 .height(),
1132 0
1133 );
1134 assert_eq!(CedarValueJson::Set(vec![]).height(), 0);
1135 assert_eq!(
1136 CedarValueJson::Record(vec![].into_iter().collect()).height(),
1137 0
1138 );
1139 }
1140
1141 #[test]
1142 fn height_set() {
1143 let val = CedarValueJson::Set(vec![CedarValueJson::Long(1), CedarValueJson::Long(2)]);
1144 assert_eq!(val.height(), 1);
1145 }
1146
1147 #[test]
1148 fn height_record() {
1149 let rec: JsonRecord = vec![("a".into(), CedarValueJson::Long(1))]
1150 .into_iter()
1151 .collect();
1152 assert_eq!(CedarValueJson::Record(rec).height(), 1);
1153 }
1154
1155 #[test]
1156 fn height_nested_set() {
1157 let inner = CedarValueJson::Set(vec![CedarValueJson::Long(1)]);
1158 let outer = CedarValueJson::Set(vec![inner]);
1159 assert_eq!(outer.height(), 2);
1160 }
1161
1162 #[test]
1163 fn height_asymmetric_set() {
1164 let val = CedarValueJson::Set(vec![
1165 CedarValueJson::Long(1),
1166 CedarValueJson::Set(vec![CedarValueJson::Long(2)]),
1167 ]);
1168 assert_eq!(val.height(), 2);
1169 }
1170
1171 #[test]
1172 fn height_extn_escape() {
1173 let val = CedarValueJson::ExtnEscape {
1174 __extn: FnAndArgs::Single {
1175 ext_fn: "decimal".into(),
1176 arg: Box::new(CedarValueJson::String("1.0".into())),
1177 },
1178 };
1179 assert_eq!(val.height(), 1);
1180 }
1181
1182 #[test]
1183 fn height_extn_escape_multi() {
1184 let val = CedarValueJson::ExtnEscape {
1185 __extn: FnAndArgs::Multi {
1186 ext_fn: "foo".into(),
1187 args: vec![CedarValueJson::Long(1), CedarValueJson::Long(2)],
1188 },
1189 };
1190 assert_eq!(val.height(), 1);
1191 }
1192
1193 #[test]
1194 fn height_nested_composites() {
1195 let extn = CedarValueJson::ExtnEscape {
1197 __extn: FnAndArgs::Single {
1198 ext_fn: "decimal".into(),
1199 arg: Box::new(CedarValueJson::String("1.0".into())),
1200 },
1201 };
1202 let set = CedarValueJson::Set(vec![extn]);
1203 let rec: JsonRecord = vec![("a".into(), set)].into_iter().collect();
1204 let val = CedarValueJson::Record(rec);
1205 assert_eq!(val.height(), 3);
1206 }
1207}