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 serde::{Deserialize, Serialize};
36use serde_with::serde_as;
37use serde_with::{DeserializeAs, SerializeAs};
38use smol_str::SmolStr;
39use std::collections::{BTreeMap, HashSet};
40use std::sync::Arc;
41
42#[cfg(feature = "wasm")]
43extern crate tsify;
44
45#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
54#[serde(untagged)]
55#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
56#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
57pub enum CedarValueJson {
58 ExprEscape {
60 #[cfg_attr(feature = "wasm", tsify(type = "__skip"))]
62 __expr: SmolStr,
63 },
64 EntityEscape {
73 __entity: TypeAndId,
75 },
76 ExtnEscape {
85 __extn: FnAndArg,
87 },
88 Bool(bool),
90 Long(i64),
92 String(#[cfg_attr(feature = "wasm", tsify(type = "string"))] SmolStr),
94 Set(Vec<CedarValueJson>),
97 Record(
100 #[cfg_attr(feature = "wasm", tsify(type = "{ [key: string]: CedarValueJson }"))] JsonRecord,
101 ),
102 Null,
105}
106
107#[serde_as]
109#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
110pub struct JsonRecord {
111 #[serde_as(as = "serde_with::MapPreventDuplicates<_, _>")]
114 #[serde(flatten)]
115 values: BTreeMap<SmolStr, CedarValueJson>,
116}
117
118impl IntoIterator for JsonRecord {
119 type Item = (SmolStr, CedarValueJson);
120 type IntoIter = <BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
121 fn into_iter(self) -> Self::IntoIter {
122 self.values.into_iter()
123 }
124}
125
126impl<'a> IntoIterator for &'a JsonRecord {
127 type Item = (&'a SmolStr, &'a CedarValueJson);
128 type IntoIter = <&'a BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
129 fn into_iter(self) -> Self::IntoIter {
130 self.values.iter()
131 }
132}
133
134impl FromIterator<(SmolStr, CedarValueJson)> for JsonRecord {
141 fn from_iter<T: IntoIterator<Item = (SmolStr, CedarValueJson)>>(iter: T) -> Self {
142 Self {
143 values: BTreeMap::from_iter(iter),
144 }
145 }
146}
147
148impl JsonRecord {
149 pub fn iter(&self) -> impl Iterator<Item = (&'_ SmolStr, &'_ CedarValueJson)> {
151 self.values.iter()
152 }
153
154 pub fn len(&self) -> usize {
156 self.values.len()
157 }
158
159 pub fn is_empty(&self) -> bool {
161 self.values.is_empty()
162 }
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
167#[serde(rename_all = "camelCase")]
168#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
169#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
170pub struct TypeAndId {
171 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
173 #[serde(rename = "type")]
174 entity_type: SmolStr,
175 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
177 id: SmolStr,
178}
179
180impl From<EntityUID> for TypeAndId {
181 fn from(euid: EntityUID) -> TypeAndId {
182 let (entity_type, eid) = euid.components();
183 TypeAndId {
184 entity_type: entity_type.to_string().into(),
185 id: AsRef::<str>::as_ref(&eid).into(),
186 }
187 }
188}
189
190impl From<&EntityUID> for TypeAndId {
191 fn from(euid: &EntityUID) -> TypeAndId {
192 TypeAndId {
193 entity_type: euid.entity_type().to_string().into(),
194 id: AsRef::<str>::as_ref(&euid.eid()).into(),
195 }
196 }
197}
198
199impl TryFrom<TypeAndId> for EntityUID {
200 type Error = crate::parser::err::ParseErrors;
201
202 fn try_from(e: TypeAndId) -> Result<EntityUID, Self::Error> {
203 Ok(EntityUID::from_components(
204 Name::from_normalized_str(&e.entity_type)?.into(),
205 Eid::new(e.id),
206 None,
207 ))
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
213#[serde(rename_all = "camelCase")]
214#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
215#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
216pub struct FnAndArg {
217 #[serde(rename = "fn")]
219 #[cfg_attr(feature = "wasm", tsify(type = "string"))]
220 pub(crate) ext_fn: SmolStr,
221 pub(crate) arg: Box<CedarValueJson>,
223}
224
225impl CedarValueJson {
226 pub fn uid(euid: &EntityUID) -> Self {
228 Self::EntityEscape {
229 __entity: TypeAndId::from(euid.clone()),
230 }
231 }
232
233 pub fn into_expr(
235 self,
236 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
237 ) -> Result<RestrictedExpr, JsonDeserializationError> {
238 match self {
239 Self::Bool(b) => Ok(RestrictedExpr::val(b)),
240 Self::Long(i) => Ok(RestrictedExpr::val(i)),
241 Self::String(s) => Ok(RestrictedExpr::val(s)),
242 Self::Set(vals) => Ok(RestrictedExpr::set(
243 vals.into_iter()
244 .map(|v| v.into_expr(ctx.clone()))
245 .collect::<Result<Vec<_>, _>>()?,
246 )),
247 Self::Record(map) => Ok(RestrictedExpr::record(
248 map.into_iter()
249 .map(|(k, v)| Ok((k, v.into_expr(ctx.clone())?)))
250 .collect::<Result<Vec<_>, JsonDeserializationError>>()?,
251 )
252 .map_err(|e| match e {
253 ExpressionConstructionError::DuplicateKey(
254 expression_construction_errors::DuplicateKeyError { key, .. },
255 ) => JsonDeserializationError::duplicate_key(ctx(), key),
256 })?),
257 Self::EntityEscape { __entity: entity } => Ok(RestrictedExpr::val(
258 EntityUID::try_from(entity.clone()).map_err(|errs| {
259 let err_msg = serde_json::to_string_pretty(&entity)
260 .unwrap_or_else(|_| format!("{:?}", &entity));
261 JsonDeserializationError::parse_escape(EscapeKind::Entity, err_msg, errs)
262 })?,
263 )),
264 Self::ExtnEscape { __extn: extn } => extn.into_expr(ctx),
265 Self::ExprEscape { .. } => Err(JsonDeserializationError::ExprTag(Box::new(ctx()))),
266 Self::Null => Err(JsonDeserializationError::Null(Box::new(ctx()))),
267 }
268 }
269
270 pub fn from_expr(expr: BorrowedRestrictedExpr<'_>) -> Result<Self, JsonSerializationError> {
272 match expr.as_ref().expr_kind() {
273 ExprKind::Lit(lit) => Ok(Self::from_lit(lit.clone())),
274 ExprKind::ExtensionFunctionApp { fn_name, args } => match args.len() {
275 0 => Err(JsonSerializationError::call_0_args(fn_name.clone())),
276 #[allow(clippy::indexing_slicing)]
278 1 => Ok(Self::ExtnEscape {
279 __extn: FnAndArg {
280 ext_fn: fn_name.to_string().into(),
281 arg: Box::new(CedarValueJson::from_expr(
282 BorrowedRestrictedExpr::new_unchecked(
284 &args[0], ),
286 )?),
287 },
288 }),
289 _ => Err(JsonSerializationError::call_2_or_more_args(fn_name.clone())),
290 },
291 ExprKind::Set(exprs) => Ok(Self::Set(
292 exprs
293 .iter()
294 .map(BorrowedRestrictedExpr::new_unchecked) .map(CedarValueJson::from_expr)
296 .collect::<Result<_, JsonSerializationError>>()?,
297 )),
298 ExprKind::Record(map) => {
299 check_for_reserved_keys(map.keys())?;
303 Ok(Self::Record(
304 map.iter()
305 .map(|(k, v)| {
306 Ok((
307 k.clone(),
308 CedarValueJson::from_expr(
309 BorrowedRestrictedExpr::new_unchecked(v),
311 )?,
312 ))
313 })
314 .collect::<Result<_, JsonSerializationError>>()?,
315 ))
316 }
317 kind => Err(JsonSerializationError::unexpected_restricted_expr_kind(
318 kind.clone(),
319 )),
320 }
321 }
322
323 pub fn from_value(value: Value) -> Result<Self, JsonSerializationError> {
334 Self::from_valuekind(value.value)
335 }
336
337 pub fn from_valuekind(value: ValueKind) -> Result<Self, JsonSerializationError> {
341 match value {
342 ValueKind::Lit(lit) => Ok(Self::from_lit(lit)),
343 ValueKind::Set(set) => Ok(Self::Set(
344 set.iter()
345 .cloned()
346 .map(Self::from_value)
347 .collect::<Result<_, _>>()?,
348 )),
349 ValueKind::Record(record) => {
350 check_for_reserved_keys(record.keys())?;
354 Ok(Self::Record(
355 record
356 .iter()
357 .map(|(k, v)| Ok((k.clone(), Self::from_value(v.clone())?)))
358 .collect::<Result<JsonRecord, JsonSerializationError>>()?,
359 ))
360 }
361 ValueKind::ExtensionValue(ev) => {
362 let ext_fn: &Name = &ev.constructor;
363 Ok(Self::ExtnEscape {
364 __extn: FnAndArg {
365 ext_fn: ext_fn.to_string().into(),
366 arg: match ev.args.as_slice() {
367 [ref expr] => Box::new(Self::from_expr(expr.as_borrowed())?),
368 [] => return Err(JsonSerializationError::call_0_args(ext_fn.clone())),
369 _ => {
370 return Err(JsonSerializationError::call_2_or_more_args(
371 ext_fn.clone(),
372 ))
373 }
374 },
375 },
376 })
377 }
378 }
379 }
380
381 pub fn from_lit(lit: Literal) -> Self {
383 match lit {
384 Literal::Bool(b) => Self::Bool(b),
385 Literal::Long(i) => Self::Long(i),
386 Literal::String(s) => Self::String(s),
387 Literal::EntityUID(euid) => Self::EntityEscape {
388 __entity: Arc::unwrap_or_clone(euid).into(),
389 },
390 }
391 }
392}
393
394fn check_for_reserved_keys<'a>(
397 mut keys: impl Iterator<Item = &'a SmolStr>,
398) -> Result<(), JsonSerializationError> {
399 let reserved_keys: HashSet<&str> = HashSet::from_iter(["__entity", "__extn", "__expr"]);
404 let collision = keys.find(|k| reserved_keys.contains(k.as_str()));
405 match collision {
406 Some(collision) => Err(JsonSerializationError::reserved_key(collision.clone())),
407 None => Ok(()),
408 }
409}
410
411impl FnAndArg {
412 pub fn into_expr(
414 self,
415 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
416 ) -> Result<RestrictedExpr, JsonDeserializationError> {
417 Ok(RestrictedExpr::call_extension_fn(
418 Name::from_normalized_str(&self.ext_fn).map_err(|errs| {
419 JsonDeserializationError::parse_escape(EscapeKind::Extension, self.ext_fn, errs)
420 })?,
421 vec![CedarValueJson::into_expr(*self.arg, ctx)?],
422 ))
423 }
424}
425
426#[derive(Debug, Clone)]
428pub struct ValueParser<'e> {
429 extensions: &'e Extensions<'e>,
431}
432
433impl<'e> ValueParser<'e> {
434 pub fn new(extensions: &'e Extensions<'e>) -> Self {
436 Self { extensions }
437 }
438
439 pub fn val_into_restricted_expr(
444 &self,
445 val: serde_json::Value,
446 expected_ty: Option<&SchemaType>,
447 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
448 ) -> Result<RestrictedExpr, JsonDeserializationError> {
449 let parse_as_unknown = |val: serde_json::Value| {
452 let extjson: ExtnValueJson = serde_json::from_value(val).ok()?;
453 match extjson {
454 ExtnValueJson::ExplicitExtnEscape {
455 __extn: FnAndArg { ext_fn, arg },
456 } if ext_fn == "unknown" => {
457 let arg = arg.into_expr(ctx.clone()).ok()?;
458 let name = arg.as_string()?;
459 Some(RestrictedExpr::unknown(Unknown::new_untyped(name.clone())))
460 }
461 _ => None, }
463 };
464 if let Some(rexpr) = parse_as_unknown(val.clone()) {
465 return Ok(rexpr);
466 }
467 match expected_ty {
469 Some(SchemaType::Entity { .. }) => {
474 let uidjson: EntityUidJson = serde_json::from_value(val)?;
475 Ok(RestrictedExpr::val(uidjson.into_euid(ctx)?))
476 }
477 Some(SchemaType::Extension { ref name, .. }) => {
482 let extjson: ExtnValueJson = serde_json::from_value(val)?;
483 self.extn_value_json_into_rexpr(extjson, name.clone(), ctx)
484 }
485 Some(expected_ty @ SchemaType::Set { element_ty }) => match val {
488 serde_json::Value::Array(elements) => Ok(RestrictedExpr::set(
489 elements
490 .into_iter()
491 .map(|element| {
492 self.val_into_restricted_expr(element, Some(element_ty), ctx.clone())
493 })
494 .collect::<Result<Vec<RestrictedExpr>, JsonDeserializationError>>()?,
495 )),
496 val => {
497 let actual_val = {
498 let jvalue: CedarValueJson = serde_json::from_value(val)?;
499 jvalue.into_expr(ctx.clone())?
500 };
501 let err = TypeMismatchError::type_mismatch(
502 expected_ty.clone(),
503 actual_val.try_type_of(self.extensions),
504 actual_val,
505 );
506 match ctx() {
507 JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
508 Err(JsonDeserializationError::EntitySchemaConformance(
509 EntitySchemaConformanceError::type_mismatch(uid, attr, err),
510 ))
511 }
512 ctx => Err(JsonDeserializationError::type_mismatch(ctx, err)),
513 }
514 }
515 },
516 Some(
520 expected_ty @ SchemaType::Record {
521 attrs: expected_attrs,
522 open_attrs,
523 },
524 ) => match val {
525 serde_json::Value::Object(mut actual_attrs) => {
526 let ctx2 = ctx.clone(); let mut_actual_attrs = &mut actual_attrs; let rexpr_pairs = expected_attrs
529 .iter()
530 .filter_map(move |(k, expected_attr_ty)| {
531 match mut_actual_attrs.remove(k.as_str()) {
532 Some(actual_attr) => {
533 match self.val_into_restricted_expr(actual_attr, Some(expected_attr_ty.schema_type()), ctx.clone()) {
534 Ok(actual_attr) => Some(Ok((k.clone(), actual_attr))),
535 Err(e) => Some(Err(e)),
536 }
537 }
538 None if expected_attr_ty.is_required() => Some(Err(JsonDeserializationError::missing_required_record_attr(ctx(), k.clone()))),
539 None => None,
540 }
541 })
542 .collect::<Result<Vec<(SmolStr, RestrictedExpr)>, JsonDeserializationError>>()?;
543
544 if !open_attrs {
545 if let Some((record_attr, _)) = actual_attrs.into_iter().next() {
548 return Err(JsonDeserializationError::unexpected_record_attr(
549 ctx2(),
550 record_attr,
551 ));
552 }
553 }
554
555 RestrictedExpr::record(rexpr_pairs).map_err(|e| match e {
560 ExpressionConstructionError::DuplicateKey(
561 expression_construction_errors::DuplicateKeyError { key, .. },
562 ) => JsonDeserializationError::duplicate_key(ctx2(), key),
563 })
564 }
565 val => {
566 let actual_val = {
567 let jvalue: CedarValueJson = serde_json::from_value(val)?;
568 jvalue.into_expr(ctx.clone())?
569 };
570 let err = TypeMismatchError::type_mismatch(
571 expected_ty.clone(),
572 actual_val.try_type_of(self.extensions),
573 actual_val,
574 );
575 match ctx() {
576 JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
577 Err(JsonDeserializationError::EntitySchemaConformance(
578 EntitySchemaConformanceError::type_mismatch(uid, attr, err),
579 ))
580 }
581 ctx => Err(JsonDeserializationError::type_mismatch(ctx, err)),
582 }
583 }
584 },
585 Some(_) | None => {
588 let jvalue: CedarValueJson = serde_json::from_value(val)?;
591 Ok(jvalue.into_expr(ctx)?)
592 }
593 }
594 }
595
596 fn extn_value_json_into_rexpr(
601 &self,
602 extnjson: ExtnValueJson,
603 expected_typename: Name,
604 ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
605 ) -> Result<RestrictedExpr, JsonDeserializationError> {
606 match extnjson {
607 ExtnValueJson::ExplicitExprEscape { __expr } => {
608 Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
609 }
610 ExtnValueJson::ExplicitExtnEscape { __extn }
611 | ExtnValueJson::ImplicitExtnEscape(__extn) => {
612 let jvalue = CedarValueJson::ExtnEscape { __extn };
614 let expr = jvalue.into_expr(ctx.clone())?;
615 match expr.expr_kind() {
616 ExprKind::ExtensionFunctionApp { .. } => Ok(expr),
617 _ => Err(JsonDeserializationError::expected_extn_value(
618 ctx(),
619 Either::Right(expr.clone().into()),
620 )),
621 }
622 }
623 ExtnValueJson::ImplicitConstructor(val) => {
624 let expected_return_type = SchemaType::Extension {
625 name: expected_typename,
626 };
627 let func = self
628 .extensions
629 .lookup_single_arg_constructor(&expected_return_type)
630 .ok_or_else(|| {
631 JsonDeserializationError::missing_implied_constructor(
632 ctx(),
633 expected_return_type,
634 )
635 })?;
636 let arg = val.into_expr(ctx.clone())?;
637 Ok(RestrictedExpr::call_extension_fn(
638 func.name().clone(),
639 vec![arg],
640 ))
641 }
642 }
643 }
644}
645
646pub trait DeserializationContext {
650 fn static_context() -> Option<JsonDeserializationErrorContext>;
653}
654
655#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
658pub struct NoStaticContext;
659
660impl DeserializationContext for NoStaticContext {
661 fn static_context() -> Option<JsonDeserializationErrorContext> {
662 None
663 }
664}
665
666#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
669#[serde(untagged)]
670#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
671#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
672pub enum EntityUidJson<Context = NoStaticContext> {
673 ExplicitExprEscape {
675 #[cfg_attr(feature = "wasm", tsify(type = "__skip"))]
677 __expr: String,
678 #[serde(skip)]
680 context: std::marker::PhantomData<Context>,
681 },
682 ExplicitEntityEscape {
684 __entity: TypeAndId,
686 },
687 ImplicitEntityEscape(TypeAndId),
690
691 FoundValue(#[cfg_attr(feature = "wasm", tsify(type = "__skip"))] serde_json::Value),
693}
694
695impl<'de, C: DeserializationContext> DeserializeAs<'de, EntityUID> for EntityUidJson<C> {
696 fn deserialize_as<D>(deserializer: D) -> Result<EntityUID, D::Error>
697 where
698 D: serde::Deserializer<'de>,
699 {
700 use serde::de::Error;
701 let context = || JsonDeserializationErrorContext::Unknown;
703 let s = EntityUidJson::<C>::deserialize(deserializer)?;
704 let euid = s.into_euid(context).map_err(Error::custom)?;
705 Ok(euid)
706 }
707}
708
709impl<C> SerializeAs<EntityUID> for EntityUidJson<C> {
710 fn serialize_as<S>(source: &EntityUID, serializer: S) -> Result<S::Ok, S::Error>
711 where
712 S: serde::Serializer,
713 {
714 let json: EntityUidJson = source.clone().into();
715 json.serialize(serializer)
716 }
717}
718
719impl<C: DeserializationContext> EntityUidJson<C> {
720 pub fn new(entity_type: impl Into<SmolStr>, id: impl Into<SmolStr>) -> Self {
724 Self::ImplicitEntityEscape(TypeAndId {
725 entity_type: entity_type.into(),
726 id: id.into(),
727 })
728 }
729
730 pub fn into_euid(
732 self,
733 dynamic_ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
734 ) -> Result<EntityUID, JsonDeserializationError> {
735 let ctx = || C::static_context().unwrap_or_else(&dynamic_ctx);
736 match self {
737 Self::ExplicitEntityEscape { __entity } | Self::ImplicitEntityEscape(__entity) => {
738 let jvalue = CedarValueJson::EntityEscape { __entity };
740 let expr = jvalue.into_expr(ctx)?;
741 match expr.expr_kind() {
742 ExprKind::Lit(Literal::EntityUID(euid)) => Ok((**euid).clone()),
743 _ => Err(JsonDeserializationError::expected_entity_ref(
744 ctx(),
745 Either::Right(expr.clone().into()),
746 )),
747 }
748 }
749 Self::FoundValue(v) => Err(JsonDeserializationError::expected_entity_ref(
750 ctx(),
751 Either::Left(v),
752 )),
753 Self::ExplicitExprEscape { __expr, .. } => {
754 Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
755 }
756 }
757 }
758}
759
760impl From<EntityUID> for EntityUidJson {
762 fn from(uid: EntityUID) -> EntityUidJson {
763 EntityUidJson::ExplicitEntityEscape {
764 __entity: uid.into(),
765 }
766 }
767}
768
769impl From<&EntityUID> for EntityUidJson {
771 fn from(uid: &EntityUID) -> EntityUidJson {
772 EntityUidJson::ExplicitEntityEscape {
773 __entity: uid.into(),
774 }
775 }
776}
777
778#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
781#[serde(untagged)]
782pub enum ExtnValueJson {
783 ExplicitExprEscape {
785 __expr: String,
787 },
788 ExplicitExtnEscape {
790 __extn: FnAndArg,
792 },
793 ImplicitExtnEscape(FnAndArg),
796 ImplicitConstructor(CedarValueJson),
802}