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: FnAndArg,
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))] => {
149 if r.values.len() >= 2 {
150 if let Some(RawCedarValueJson::String(fn_name)) = r.values.get("fn")
151 {
152 if let Some(arg) = r.values.get("arg") {
153 return Self::ExtnEscape {
154 __extn: FnAndArg {
155 ext_fn: fn_name.clone(),
156 arg: Box::new(arg.clone().into()),
157 },
158 };
159 }
160 }
161 }
162 }
163 [("__expr", RawCedarValueJson::String(s))] => {
164 return Self::ExprEscape { __expr: s.clone() };
165 }
166 [("__entity", RawCedarValueJson::Record(r))] => {
167 if r.values.len() >= 2 {
168 if let Some(RawCedarValueJson::String(ty)) = r.values.get("type") {
169 if let Some(RawCedarValueJson::String(id)) = r.values.get("id")
170 {
171 return Self::EntityEscape {
172 __entity: TypeAndId {
173 entity_type: ty.clone(),
174 id: id.clone(),
175 },
176 };
177 }
178 }
179 }
180 }
181 _ => {}
182 }
183 }
184 Self::Record(r.into())
185 }
186 RawCedarValueJson::Set(s) => Self::Set(s.into_iter().map(Into::into).collect()),
187 RawCedarValueJson::String(s) => Self::String(s),
188 }
189 }
190}
191
192#[serde_as]
193#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
194struct RawJsonRecord {
195 #[serde_as(as = "serde_with::MapPreventDuplicates<_, _>")]
198 #[serde(flatten)]
199 values: BTreeMap<SmolStr, RawCedarValueJson>,
200}
201
202impl From<RawJsonRecord> for JsonRecord {
203 fn from(value: RawJsonRecord) -> Self {
204 JsonRecord {
205 values: value
206 .values
207 .into_iter()
208 .map(|(k, v)| (k, v.into()))
209 .collect(),
210 }
211 }
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
216pub struct JsonRecord {
217 #[serde(flatten)]
220 values: BTreeMap<SmolStr, CedarValueJson>,
221}
222
223impl IntoIterator for JsonRecord {
224 type Item = (SmolStr, CedarValueJson);
225 type IntoIter = <BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
226 fn into_iter(self) -> Self::IntoIter {
227 self.values.into_iter()
228 }
229}
230
231impl<'a> IntoIterator for &'a JsonRecord {
232 type Item = (&'a SmolStr, &'a CedarValueJson);
233 type IntoIter = <&'a BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
234 fn into_iter(self) -> Self::IntoIter {
235 self.values.iter()
236 }
237}
238
239impl FromIterator<(SmolStr, CedarValueJson)> for JsonRecord {
246 fn from_iter<T: IntoIterator<Item = (SmolStr, CedarValueJson)>>(iter: T) -> Self {
247 Self {
248 values: BTreeMap::from_iter(iter),
249 }
250 }
251}
252
253impl JsonRecord {
254 pub fn iter(&self) -> impl Iterator<Item = (&'_ SmolStr, &'_ CedarValueJson)> {
256 self.values.iter()
257 }
258
259 pub fn len(&self) -> usize {
261 self.values.len()
262 }
263
264 pub fn is_empty(&self) -> bool {
266 self.values.is_empty()
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
272#[serde(rename_all = "camelCase")]
273#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
274#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
275pub struct TypeAndId {
276 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
278 #[serde(rename = "type")]
279 entity_type: SmolStr,
280 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
282 id: SmolStr,
283}
284
285impl From<EntityUID> for TypeAndId {
286 fn from(euid: EntityUID) -> TypeAndId {
287 let (entity_type, eid) = euid.components();
288 TypeAndId {
289 entity_type: entity_type.to_smolstr(),
290 id: AsRef::<str>::as_ref(&eid).into(),
291 }
292 }
293}
294
295impl From<&EntityUID> for TypeAndId {
296 fn from(euid: &EntityUID) -> TypeAndId {
297 TypeAndId {
298 entity_type: euid.entity_type().to_smolstr(),
299 id: AsRef::<str>::as_ref(&euid.eid()).into(),
300 }
301 }
302}
303
304impl TryFrom<TypeAndId> for EntityUID {
305 type Error = crate::parser::err::ParseErrors;
306
307 fn try_from(e: TypeAndId) -> Result<EntityUID, crate::parser::err::ParseErrors> {
308 Ok(EntityUID::from_components(
309 Name::from_normalized_str(&e.entity_type)?.into(),
310 Eid::new(e.id),
311 None,
312 ))
313 }
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
318#[serde(rename_all = "camelCase")]
319#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
320#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
321pub struct FnAndArg {
322 #[serde(rename = "fn")]
324 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
325 pub(crate) ext_fn: SmolStr,
326 pub(crate) arg: Box<CedarValueJson>,
328}
329
330impl CedarValueJson {
331 pub fn uid(euid: &EntityUID) -> Self {
333 Self::EntityEscape {
334 __entity: TypeAndId::from(euid.clone()),
335 }
336 }
337
338 pub fn into_expr(
340 self,
341 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
342 ) -> Result<RestrictedExpr, JsonDeserializationError> {
343 match self {
344 Self::Bool(b) => Ok(RestrictedExpr::val(b)),
345 Self::Long(i) => Ok(RestrictedExpr::val(i)),
346 Self::String(s) => Ok(RestrictedExpr::val(s)),
347 Self::Set(vals) => Ok(RestrictedExpr::set(
348 vals.into_iter()
349 .map(|v| v.into_expr(ctx.clone()))
350 .collect::<Result<Vec<_>, _>>()?,
351 )),
352 Self::Record(map) => Ok(RestrictedExpr::record(
353 map.into_iter()
354 .map(|(k, v)| Ok((k, v.into_expr(ctx.clone())?)))
355 .collect::<Result<Vec<_>, JsonDeserializationError>>()?,
356 )
357 .map_err(|e| match e {
358 ExpressionConstructionError::DuplicateKey(
359 expression_construction_errors::DuplicateKeyError { key, .. },
360 ) => JsonDeserializationError::duplicate_key(ctx(), key),
361 })?),
362 Self::EntityEscape { __entity: entity } => Ok(RestrictedExpr::val(
363 EntityUID::try_from(entity.clone()).map_err(|errs| {
364 let err_msg = serde_json::to_string_pretty(&entity)
365 .unwrap_or_else(|_| format!("{:?}", &entity));
366 JsonDeserializationError::parse_escape(EscapeKind::Entity, err_msg, errs)
367 })?,
368 )),
369 Self::ExtnEscape { __extn: extn } => extn.into_expr(ctx),
370 Self::ExprEscape { .. } => Err(JsonDeserializationError::ExprTag(Box::new(ctx()))),
371 Self::Null => Err(JsonDeserializationError::Null(Box::new(ctx()))),
372 }
373 }
374
375 pub fn from_expr(expr: BorrowedRestrictedExpr<'_>) -> Result<Self, JsonSerializationError> {
377 match expr.as_ref().expr_kind() {
378 ExprKind::Lit(lit) => Ok(Self::from_lit(lit.clone())),
379 ExprKind::ExtensionFunctionApp { fn_name, args } => match args.len() {
380 0 => Err(JsonSerializationError::call_0_args(fn_name.clone())),
381 #[allow(clippy::indexing_slicing)]
383 1 => Ok(Self::ExtnEscape {
384 __extn: FnAndArg {
385 ext_fn: fn_name.to_smolstr(),
386 arg: Box::new(CedarValueJson::from_expr(
387 BorrowedRestrictedExpr::new_unchecked(
389 &args[0], ),
391 )?),
392 },
393 }),
394 _ => Err(JsonSerializationError::call_2_or_more_args(fn_name.clone())),
395 },
396 ExprKind::Set(exprs) => Ok(Self::Set(
397 exprs
398 .iter()
399 .map(BorrowedRestrictedExpr::new_unchecked) .map(CedarValueJson::from_expr)
401 .collect::<Result<_, JsonSerializationError>>()?,
402 )),
403 ExprKind::Record(map) => {
404 check_for_reserved_keys(map.keys())?;
408 Ok(Self::Record(
409 map.iter()
410 .map(|(k, v)| {
411 Ok((
412 k.clone(),
413 CedarValueJson::from_expr(
414 BorrowedRestrictedExpr::new_unchecked(v),
416 )?,
417 ))
418 })
419 .collect::<Result<_, JsonSerializationError>>()?,
420 ))
421 }
422 kind => Err(JsonSerializationError::unexpected_restricted_expr_kind(
423 kind.clone(),
424 )),
425 }
426 }
427
428 pub fn from_value(value: Value) -> Result<Self, JsonSerializationError> {
439 Self::from_valuekind(value.value)
440 }
441
442 pub fn from_valuekind(value: ValueKind) -> Result<Self, JsonSerializationError> {
446 match value {
447 ValueKind::Lit(lit) => Ok(Self::from_lit(lit)),
448 ValueKind::Set(set) => Ok(Self::Set(
449 set.iter()
450 .cloned()
451 .map(Self::from_value)
452 .collect::<Result<_, _>>()?,
453 )),
454 ValueKind::Record(record) => {
455 check_for_reserved_keys(record.keys())?;
459 Ok(Self::Record(
460 record
461 .iter()
462 .map(|(k, v)| Ok((k.clone(), Self::from_value(v.clone())?)))
463 .collect::<Result<JsonRecord, JsonSerializationError>>()?,
464 ))
465 }
466 ValueKind::ExtensionValue(ev) => {
467 let ext_func = &ev.func;
468 Ok(Self::ExtnEscape {
469 __extn: FnAndArg {
470 ext_fn: ext_func.to_smolstr(),
471 arg: match ev.args.as_slice() {
472 [ref expr] => Box::new(Self::from_expr(expr.as_borrowed())?),
473 [] => {
474 return Err(JsonSerializationError::call_0_args(ext_func.clone()))
475 }
476 _ => {
477 return Err(JsonSerializationError::call_2_or_more_args(
478 ext_func.clone(),
479 ))
480 }
481 },
482 },
483 })
484 }
485 }
486 }
487
488 pub fn from_lit(lit: Literal) -> Self {
490 match lit {
491 Literal::Bool(b) => Self::Bool(b),
492 Literal::Long(i) => Self::Long(i),
493 Literal::String(s) => Self::String(s),
494 Literal::EntityUID(euid) => Self::EntityEscape {
495 __entity: Arc::unwrap_or_clone(euid).into(),
496 },
497 }
498 }
499
500 pub fn sub_entity_literals(
502 self,
503 mapping: &BTreeMap<EntityUID, EntityUID>,
504 ) -> Result<Self, JsonDeserializationError> {
505 match self {
506 CedarValueJson::ExprEscape { __expr } => Err(JsonDeserializationError::ExprTag(
508 Box::new(JsonDeserializationErrorContext::Unknown),
509 )),
510 CedarValueJson::EntityEscape { __entity } => {
511 let euid = EntityUID::try_from(__entity.clone());
512 match euid {
513 Ok(euid) => match mapping.get(&euid) {
514 Some(new_euid) => Ok(CedarValueJson::EntityEscape {
515 __entity: new_euid.into(),
516 }),
517 None => Ok(CedarValueJson::EntityEscape { __entity }),
518 },
519 Err(_) => Ok(CedarValueJson::EntityEscape { __entity }),
520 }
521 }
522 CedarValueJson::ExtnEscape { __extn } => Ok(CedarValueJson::ExtnEscape {
523 __extn: FnAndArg {
524 ext_fn: __extn.ext_fn,
525 arg: Box::new((*__extn.arg).sub_entity_literals(mapping)?),
526 },
527 }),
528 v @ CedarValueJson::Bool(_) => Ok(v),
529 v @ CedarValueJson::Long(_) => Ok(v),
530 v @ CedarValueJson::String(_) => Ok(v),
531 CedarValueJson::Set(v) => Ok(CedarValueJson::Set(
532 v.into_iter()
533 .map(|e| e.sub_entity_literals(mapping))
534 .collect::<Result<Vec<_>, _>>()?,
535 )),
536 CedarValueJson::Record(r) => {
537 let mut new_m = BTreeMap::new();
538 for (k, v) in r.values {
539 new_m.insert(k, v.sub_entity_literals(mapping)?);
540 }
541 Ok(CedarValueJson::Record(JsonRecord { values: new_m }))
542 }
543 v @ CedarValueJson::Null => Ok(v),
544 }
545 }
546}
547
548fn check_for_reserved_keys<'a>(
551 mut keys: impl Iterator<Item = &'a SmolStr>,
552) -> Result<(), JsonSerializationError> {
553 let reserved_keys: HashSet<&str> = HashSet::from_iter(["__entity", "__extn", "__expr"]);
558 let collision = keys.find(|k| reserved_keys.contains(k.as_str()));
559 match collision {
560 Some(collision) => Err(JsonSerializationError::reserved_key(collision.clone())),
561 None => Ok(()),
562 }
563}
564
565impl FnAndArg {
566 pub fn into_expr(
568 self,
569 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
570 ) -> Result<RestrictedExpr, JsonDeserializationError> {
571 Ok(RestrictedExpr::call_extension_fn(
572 Name::from_normalized_str(&self.ext_fn).map_err(|errs| {
573 JsonDeserializationError::parse_escape(EscapeKind::Extension, self.ext_fn, errs)
574 })?,
575 vec![CedarValueJson::into_expr(*self.arg, ctx)?],
576 ))
577 }
578}
579
580#[derive(Debug, Clone)]
582pub struct ValueParser<'e> {
583 extensions: &'e Extensions<'e>,
585}
586
587impl<'e> ValueParser<'e> {
588 pub fn new(extensions: &'e Extensions<'e>) -> Self {
590 Self { extensions }
591 }
592
593 pub fn val_into_restricted_expr(
598 &self,
599 val: serde_json::Value,
600 expected_ty: Option<&SchemaType>,
601 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
602 ) -> Result<RestrictedExpr, JsonDeserializationError> {
603 let parse_as_unknown = |val: serde_json::Value| {
606 let extjson: ExtnValueJson = serde_json::from_value(val).ok()?;
607 match extjson {
608 ExtnValueJson::ExplicitExtnEscape {
609 __extn: FnAndArg { ext_fn, arg },
610 } if ext_fn == "unknown" => {
611 let arg = arg.into_expr(ctx.clone()).ok()?;
612 let name = arg.as_string()?;
613 Some(RestrictedExpr::unknown(Unknown::new_untyped(name.clone())))
614 }
615 _ => None, }
617 };
618 if let Some(rexpr) = parse_as_unknown(val.clone()) {
619 return Ok(rexpr);
620 }
621 match expected_ty {
623 Some(SchemaType::Entity { .. }) => {
628 let uidjson: EntityUidJson = serde_json::from_value(val)?;
629 Ok(RestrictedExpr::val(uidjson.into_euid(ctx)?))
630 }
631 Some(SchemaType::Extension { ref name, .. }) => {
636 let extjson: ExtnValueJson = serde_json::from_value(val)?;
637 self.extn_value_json_into_rexpr(extjson, name.clone(), ctx)
638 }
639 Some(expected_ty @ SchemaType::Set { element_ty }) => match val {
642 serde_json::Value::Array(elements) => Ok(RestrictedExpr::set(
643 elements
644 .into_iter()
645 .map(|element| {
646 self.val_into_restricted_expr(element, Some(element_ty), ctx.clone())
647 })
648 .collect::<Result<Vec<RestrictedExpr>, JsonDeserializationError>>()?,
649 )),
650 val => {
651 let actual_val = {
652 let jvalue: CedarValueJson = serde_json::from_value(val)?;
653 jvalue.into_expr(ctx.clone())?
654 };
655 let err = TypeMismatchError::type_mismatch(
656 expected_ty.clone(),
657 actual_val.try_type_of(self.extensions),
658 actual_val,
659 );
660 match ctx() {
661 JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
662 Err(JsonDeserializationError::EntitySchemaConformance(
663 EntitySchemaConformanceError::type_mismatch(
664 uid,
665 attr,
666 crate::entities::conformance::err::AttrOrTag::Attr,
667 err,
668 ),
669 ))
670 }
671 ctx => Err(JsonDeserializationError::type_mismatch(ctx, err)),
672 }
673 }
674 },
675 Some(
679 expected_ty @ SchemaType::Record {
680 attrs: expected_attrs,
681 open_attrs,
682 },
683 ) => match val {
684 serde_json::Value::Object(mut actual_attrs) => {
685 let ctx2 = ctx.clone(); let mut_actual_attrs = &mut actual_attrs; let rexpr_pairs = expected_attrs
688 .iter()
689 .filter_map(move |(k, expected_attr_ty)| {
690 match mut_actual_attrs.remove(k.as_str()) {
691 Some(actual_attr) => {
692 match self.val_into_restricted_expr(actual_attr, Some(expected_attr_ty.schema_type()), ctx.clone()) {
693 Ok(actual_attr) => Some(Ok((k.clone(), actual_attr))),
694 Err(e) => Some(Err(e)),
695 }
696 }
697 None if expected_attr_ty.is_required() => Some(Err(JsonDeserializationError::missing_required_record_attr(ctx(), k.clone()))),
698 None => None,
699 }
700 })
701 .collect::<Result<Vec<(SmolStr, RestrictedExpr)>, JsonDeserializationError>>()?;
702
703 if !open_attrs {
704 if let Some((record_attr, _)) = actual_attrs.into_iter().next() {
707 return Err(JsonDeserializationError::unexpected_record_attr(
708 ctx2(),
709 record_attr,
710 ));
711 }
712 }
713
714 RestrictedExpr::record(rexpr_pairs).map_err(|e| match e {
719 ExpressionConstructionError::DuplicateKey(
720 expression_construction_errors::DuplicateKeyError { key, .. },
721 ) => JsonDeserializationError::duplicate_key(ctx2(), key),
722 })
723 }
724 val => {
725 let actual_val = {
726 let jvalue: CedarValueJson = serde_json::from_value(val)?;
727 jvalue.into_expr(ctx.clone())?
728 };
729 let err = TypeMismatchError::type_mismatch(
730 expected_ty.clone(),
731 actual_val.try_type_of(self.extensions),
732 actual_val,
733 );
734 match ctx() {
735 JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
736 Err(JsonDeserializationError::EntitySchemaConformance(
737 EntitySchemaConformanceError::type_mismatch(
738 uid,
739 attr,
740 crate::entities::conformance::err::AttrOrTag::Attr,
741 err,
742 ),
743 ))
744 }
745 ctx => Err(JsonDeserializationError::type_mismatch(ctx, err)),
746 }
747 }
748 },
749 Some(_) | None => {
752 let jvalue: CedarValueJson = serde_json::from_value(val)?;
755 Ok(jvalue.into_expr(ctx)?)
756 }
757 }
758 }
759
760 fn extn_value_json_into_rexpr(
765 &self,
766 extnjson: ExtnValueJson,
767 expected_typename: Name,
768 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
769 ) -> Result<RestrictedExpr, JsonDeserializationError> {
770 match extnjson {
771 ExtnValueJson::ExplicitExprEscape { __expr } => {
772 Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
773 }
774 ExtnValueJson::ExplicitExtnEscape { __extn }
775 | ExtnValueJson::ImplicitExtnEscape(__extn) => {
776 let jvalue = CedarValueJson::ExtnEscape { __extn };
778 let expr = jvalue.into_expr(ctx.clone())?;
779 match expr.expr_kind() {
780 ExprKind::ExtensionFunctionApp { .. } => Ok(expr),
781 _ => Err(JsonDeserializationError::expected_extn_value(
782 ctx(),
783 Either::Right(expr.clone().into()),
784 )),
785 }
786 }
787 ExtnValueJson::ImplicitConstructor(val) => {
788 let expected_return_type = SchemaType::Extension {
789 name: expected_typename,
790 };
791 let func = self
792 .extensions
793 .lookup_single_arg_constructor(&expected_return_type)
794 .ok_or_else(|| {
795 JsonDeserializationError::missing_implied_constructor(
796 ctx(),
797 expected_return_type,
798 )
799 })?;
800 let arg = val.into_expr(ctx.clone())?;
801 Ok(RestrictedExpr::call_extension_fn(
802 func.name().clone(),
803 vec![arg],
804 ))
805 }
806 }
807 }
808}
809
810pub trait DeserializationContext {
814 fn static_context() -> Option<JsonDeserializationErrorContext>;
817}
818
819#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
822pub struct NoStaticContext;
823
824impl DeserializationContext for NoStaticContext {
825 fn static_context() -> Option<JsonDeserializationErrorContext> {
826 None
827 }
828}
829
830#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
833#[serde(untagged)]
834#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
835#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
836pub enum EntityUidJson<Context = NoStaticContext> {
837 ExplicitExprEscape {
839 #[cfg_attr(feature = "wasm", tsify(type = "__skip"))]
841 __expr: String,
842 #[serde(skip)]
844 context: std::marker::PhantomData<Context>,
845 },
846 ExplicitEntityEscape {
848 __entity: TypeAndId,
850 },
851 ImplicitEntityEscape(TypeAndId),
854
855 FoundValue(#[cfg_attr(feature = "wasm", tsify(type = "__skip"))] serde_json::Value),
857}
858
859impl<'de, C: DeserializationContext> DeserializeAs<'de, EntityUID> for EntityUidJson<C> {
860 fn deserialize_as<D>(deserializer: D) -> Result<EntityUID, D::Error>
861 where
862 D: serde::Deserializer<'de>,
863 {
864 use serde::de::Error;
865 let context = || JsonDeserializationErrorContext::Unknown;
867 let s = EntityUidJson::<C>::deserialize(deserializer)?;
868 let euid = s.into_euid(context).map_err(Error::custom)?;
869 Ok(euid)
870 }
871}
872
873impl<C> SerializeAs<EntityUID> for EntityUidJson<C> {
874 fn serialize_as<S>(source: &EntityUID, serializer: S) -> Result<S::Ok, S::Error>
875 where
876 S: serde::Serializer,
877 {
878 let json: EntityUidJson = source.clone().into();
879 json.serialize(serializer)
880 }
881}
882
883impl<C: DeserializationContext> EntityUidJson<C> {
884 pub fn new(entity_type: impl Into<SmolStr>, id: impl Into<SmolStr>) -> Self {
888 Self::ImplicitEntityEscape(TypeAndId {
889 entity_type: entity_type.into(),
890 id: id.into(),
891 })
892 }
893
894 pub fn into_euid(
896 self,
897 dynamic_ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
898 ) -> Result<EntityUID, JsonDeserializationError> {
899 let ctx = || C::static_context().unwrap_or_else(&dynamic_ctx);
900 match self {
901 Self::ExplicitEntityEscape { __entity } | Self::ImplicitEntityEscape(__entity) => {
902 let jvalue = CedarValueJson::EntityEscape { __entity };
904 let expr = jvalue.into_expr(ctx)?;
905 match expr.expr_kind() {
906 ExprKind::Lit(Literal::EntityUID(euid)) => Ok((**euid).clone()),
907 _ => Err(JsonDeserializationError::expected_entity_ref(
908 ctx(),
909 Either::Right(expr.clone().into()),
910 )),
911 }
912 }
913 Self::FoundValue(v) => Err(JsonDeserializationError::expected_entity_ref(
914 ctx(),
915 Either::Left(v),
916 )),
917 Self::ExplicitExprEscape { __expr, .. } => {
918 Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
919 }
920 }
921 }
922}
923
924impl From<EntityUID> for EntityUidJson {
926 fn from(uid: EntityUID) -> EntityUidJson {
927 EntityUidJson::ExplicitEntityEscape {
928 __entity: uid.into(),
929 }
930 }
931}
932
933impl From<&EntityUID> for EntityUidJson {
935 fn from(uid: &EntityUID) -> EntityUidJson {
936 EntityUidJson::ExplicitEntityEscape {
937 __entity: uid.into(),
938 }
939 }
940}
941
942#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
945#[serde(untagged)]
946pub enum ExtnValueJson {
947 ExplicitExprEscape {
949 __expr: String,
951 },
952 ExplicitExtnEscape {
954 __extn: FnAndArg,
956 },
957 ImplicitExtnEscape(FnAndArg),
960 ImplicitConstructor(CedarValueJson),
966}