1use facet::{Facet, OpaqueSerialize, PtrConst};
7use facet_core::Shape;
8use std::collections::HashMap;
9
10#[derive(Facet, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
21#[facet(transparent)]
22pub struct SchemaHash(pub u64);
23
24#[derive(Facet, Clone, Copy, PartialEq, Eq, Hash, Debug)]
28pub struct CycleSchemaIndex(u64);
29
30impl CycleSchemaIndex {
31 pub fn first() -> Self {
33 Self(1)
34 }
35
36 pub fn next(&mut self) -> Self {
38 let current = *self;
39 self.0 += 1;
40 current
41 }
42}
43
44#[derive(Facet, Clone, PartialEq, Eq, Hash, Debug)]
53#[facet(transparent)]
54pub struct TypeParamName(pub String);
55
56impl TypeParamName {
57 pub fn as_str(&self) -> &str {
59 &self.0
60 }
61}
62
63#[derive(Facet, Clone, Debug, PartialEq, Eq, Hash)]
70#[repr(u8)]
71#[facet(tag = "tag", rename_all = "snake_case")]
72pub enum TypeRef<Id = SchemaHash> {
73 Concrete {
75 type_id: Id,
76 args: Vec<TypeRef<Id>>,
78 },
79 Var { name: TypeParamName },
82}
83
84impl<Id> TypeRef<Id> {
85 pub fn concrete(type_id: Id) -> Self {
87 TypeRef::Concrete {
88 type_id,
89 args: Vec::new(),
90 }
91 }
92
93 pub fn generic(type_id: Id, args: Vec<TypeRef<Id>>) -> Self {
95 TypeRef::Concrete { type_id, args }
96 }
97
98 pub fn collect_ids(&self, out: &mut Vec<Id>)
100 where
101 Id: Copy,
102 {
103 match self {
104 TypeRef::Concrete { type_id, args } => {
105 out.push(*type_id);
106 for arg in args {
107 arg.collect_ids(out);
108 }
109 }
110 TypeRef::Var { .. } => {}
111 }
112 }
113
114 pub fn expect_concrete_id(&self) -> &Id {
116 match self {
117 TypeRef::Concrete { type_id, args } if args.is_empty() => type_id,
118 TypeRef::Concrete { .. } => panic!("TypeRef::expect_concrete_id: has type args"),
119 TypeRef::Var { .. } => panic!("TypeRef::expect_concrete_id: is a type variable"),
120 }
121 }
122
123 pub fn map<OtherId, F: Fn(Id) -> OtherId + Copy>(self, f: F) -> TypeRef<OtherId> {
125 match self {
126 TypeRef::Concrete { type_id, args } => TypeRef::Concrete {
127 type_id: f(type_id),
128 args: args.into_iter().map(|a| a.map(f)).collect(),
129 },
130 TypeRef::Var { name } => TypeRef::Var { name },
131 }
132 }
133
134 pub fn try_map<OtherId, E, F: Fn(Id) -> Result<OtherId, E> + Copy>(
136 self,
137 f: &F,
138 ) -> Result<TypeRef<OtherId>, E> {
139 match self {
140 TypeRef::Concrete { type_id, args } => Ok(TypeRef::Concrete {
141 type_id: f(type_id)?,
142 args: args
143 .into_iter()
144 .map(|a| a.try_map(f))
145 .collect::<Result<_, _>>()?,
146 }),
147 TypeRef::Var { name } => Ok(TypeRef::Var { name }),
148 }
149 }
150}
151
152impl TypeRef {
153 pub fn resolve_kind(&self, registry: &SchemaRegistry) -> Option<SchemaKind> {
162 match self {
163 TypeRef::Var { .. } => None,
164 TypeRef::Concrete { type_id, args } => {
165 let schema = registry.get(type_id)?;
166 if args.is_empty() {
167 return Some(schema.kind.clone());
168 }
169 let subst: HashMap<&TypeParamName, &TypeRef> =
171 schema.type_params.iter().zip(args.iter()).collect();
172 let kind = schema
173 .kind
174 .clone()
175 .try_map_type_refs(&mut |tr| -> Result<TypeRef, std::convert::Infallible> {
176 Ok(match tr {
177 TypeRef::Var { ref name } => match subst.get(name) {
178 Some(concrete) => (*concrete).clone(),
179 None => tr,
180 },
181 other => other,
182 })
183 })
184 .unwrap(); Some(kind)
186 }
187 }
188 }
189}
190
191#[derive(Facet, Clone, Copy, PartialEq, Eq, Hash, Debug)]
194#[repr(u8)]
195pub enum MixedId {
196 Final(SchemaHash),
198 Temp(CycleSchemaIndex),
201}
202
203#[derive(Facet, Clone, Debug)]
205pub struct Schema<Id = SchemaHash> {
206 pub id: Id,
208
209 #[facet(default)]
211 pub type_params: Vec<TypeParamName>,
212
213 pub kind: SchemaKind<Id>,
215}
216
217impl Schema {
218 pub fn name(&self) -> Option<&str> {
221 match &self.kind {
222 SchemaKind::Struct { name, .. } | SchemaKind::Enum { name, .. } => Some(name.as_str()),
223 _ => None,
224 }
225 }
226}
227
228#[derive(Facet, Clone, Debug)]
230#[repr(u8)]
231#[facet(tag = "tag", rename_all = "snake_case")]
232pub enum SchemaKind<Id = SchemaHash> {
233 Struct {
234 name: String,
237 fields: Vec<FieldSchema<Id>>,
238 },
239 Enum {
240 name: String,
243 variants: Vec<VariantSchema<Id>>,
244 },
245 Tuple {
246 elements: Vec<TypeRef<Id>>,
247 },
248 List {
249 element: TypeRef<Id>,
250 },
251 Map {
252 key: TypeRef<Id>,
253 value: TypeRef<Id>,
254 },
255 Array {
256 element: TypeRef<Id>,
257 length: u64,
258 },
259 Option {
260 element: TypeRef<Id>,
261 },
262 Channel {
263 direction: ChannelDirection,
264 element: TypeRef<Id>,
265 },
266 Primitive {
267 primitive_type: PrimitiveType,
268 },
269}
270
271impl<Id> SchemaKind<Id> {
272 pub fn for_each_type_ref(&self, f: &mut impl FnMut(&TypeRef<Id>)) {
274 match self {
275 Self::Primitive { .. } => {}
276 Self::Struct { fields, .. } => {
277 for field in fields {
278 field.for_each_type_ref(f);
279 }
280 }
281 Self::Enum { variants, .. } => {
282 for variant in variants {
283 variant.for_each_type_ref(f);
284 }
285 }
286 Self::Tuple { elements } => {
287 for elem in elements {
288 f(elem);
289 }
290 }
291 Self::List { element }
292 | Self::Option { element }
293 | Self::Array { element, .. }
294 | Self::Channel { element, .. } => f(element),
295 Self::Map { key, value } => {
296 f(key);
297 f(value);
298 }
299 }
300 }
301
302 pub fn try_map_type_refs<OtherId, E>(
304 self,
305 f: &mut impl FnMut(TypeRef<Id>) -> Result<TypeRef<OtherId>, E>,
306 ) -> Result<SchemaKind<OtherId>, E> {
307 Ok(match self {
308 Self::Primitive { primitive_type } => SchemaKind::Primitive { primitive_type },
309 Self::Struct { name, fields } => SchemaKind::Struct {
310 name,
311 fields: fields
312 .into_iter()
313 .map(|field| field.try_map_type_ref(f))
314 .collect::<Result<_, _>>()?,
315 },
316 Self::Enum { name, variants } => SchemaKind::Enum {
317 name,
318 variants: variants
319 .into_iter()
320 .map(|v| v.try_map_type_refs(f))
321 .collect::<Result<_, _>>()?,
322 },
323 Self::Tuple { elements } => SchemaKind::Tuple {
324 elements: elements.into_iter().map(f).collect::<Result<_, _>>()?,
325 },
326 Self::List { element } => SchemaKind::List {
327 element: f(element)?,
328 },
329 Self::Map { key, value } => SchemaKind::Map {
330 key: f(key)?,
331 value: f(value)?,
332 },
333 Self::Array { element, length } => SchemaKind::Array {
334 element: f(element)?,
335 length,
336 },
337 Self::Option { element } => SchemaKind::Option {
338 element: f(element)?,
339 },
340 Self::Channel { direction, element } => SchemaKind::Channel {
341 direction,
342 element: f(element)?,
343 },
344 })
345 }
346}
347
348impl<Id> FieldSchema<Id> {
349 pub fn for_each_type_ref(&self, f: &mut impl FnMut(&TypeRef<Id>)) {
351 f(&self.type_ref);
352 }
353
354 pub fn try_map_type_ref<OtherId, E>(
356 self,
357 f: &mut impl FnMut(TypeRef<Id>) -> Result<TypeRef<OtherId>, E>,
358 ) -> Result<FieldSchema<OtherId>, E> {
359 Ok(FieldSchema {
360 name: self.name,
361 type_ref: f(self.type_ref)?,
362 required: self.required,
363 })
364 }
365}
366
367impl<Id> VariantSchema<Id> {
368 pub fn for_each_type_ref(&self, f: &mut impl FnMut(&TypeRef<Id>)) {
370 self.payload.for_each_type_ref(f);
371 }
372
373 pub fn try_map_type_refs<OtherId, E>(
375 self,
376 f: &mut impl FnMut(TypeRef<Id>) -> Result<TypeRef<OtherId>, E>,
377 ) -> Result<VariantSchema<OtherId>, E> {
378 Ok(VariantSchema {
379 name: self.name,
380 index: self.index,
381 payload: self.payload.try_map_type_refs(f)?,
382 })
383 }
384}
385
386impl<Id> VariantPayload<Id> {
387 pub fn for_each_type_ref(&self, f: &mut impl FnMut(&TypeRef<Id>)) {
389 match self {
390 Self::Unit => {}
391 Self::Newtype { type_ref } => f(type_ref),
392 Self::Tuple { types } => {
393 for t in types {
394 f(t);
395 }
396 }
397 Self::Struct { fields } => {
398 for field in fields {
399 field.for_each_type_ref(f);
400 }
401 }
402 }
403 }
404
405 pub fn try_map_type_refs<OtherId, E>(
407 self,
408 f: &mut impl FnMut(TypeRef<Id>) -> Result<TypeRef<OtherId>, E>,
409 ) -> Result<VariantPayload<OtherId>, E> {
410 Ok(match self {
411 Self::Unit => VariantPayload::Unit,
412 Self::Newtype { type_ref } => VariantPayload::Newtype {
413 type_ref: f(type_ref)?,
414 },
415 Self::Tuple { types } => VariantPayload::Tuple {
416 types: types.into_iter().map(f).collect::<Result<_, _>>()?,
417 },
418 Self::Struct { fields } => VariantPayload::Struct {
419 fields: fields
420 .into_iter()
421 .map(|field| field.try_map_type_ref(f))
422 .collect::<Result<_, _>>()?,
423 },
424 })
425 }
426}
427
428#[derive(Facet, Clone, Copy, PartialEq, Eq, Debug)]
430#[repr(u8)]
431#[facet(tag = "tag", rename_all = "snake_case")]
432pub enum ChannelDirection {
433 Tx,
435 Rx,
437}
438
439pub type MixedSchema = Schema<MixedId>;
441pub type MixedSchemaKind = SchemaKind<MixedId>;
442
443#[derive(Facet, Clone, Debug)]
445pub struct FieldSchema<Id = SchemaHash> {
446 pub name: String,
447 pub type_ref: TypeRef<Id>,
448 pub required: bool,
449}
450
451#[derive(Facet, Clone, Debug)]
453pub struct VariantSchema<Id = SchemaHash> {
454 pub name: String,
455 pub index: u32,
456 pub payload: VariantPayload<Id>,
457}
458
459#[derive(Facet, Clone, Debug)]
461#[repr(u8)]
462#[facet(tag = "tag", rename_all = "snake_case")]
463pub enum VariantPayload<Id = SchemaHash> {
464 Unit,
465 Newtype { type_ref: TypeRef<Id> },
466 Tuple { types: Vec<TypeRef<Id>> },
467 Struct { fields: Vec<FieldSchema<Id>> },
468}
469
470#[derive(Facet, Clone, Copy, PartialEq, Eq, Debug)]
472#[repr(u8)]
473#[facet(tag = "tag", rename_all = "snake_case")]
474pub enum PrimitiveType {
475 Bool,
476 U8,
477 U16,
478 U32,
479 U64,
480 U128,
481 I8,
482 I16,
483 I32,
484 I64,
485 I128,
486 F32,
487 F64,
488 Char,
489 String,
490 Unit,
491 Never,
492 Bytes,
493 Payload,
497}
498
499impl PrimitiveType {
504 fn hash_tag(self) -> &'static str {
506 match self {
507 PrimitiveType::Bool => "bool",
508 PrimitiveType::U8 => "u8",
509 PrimitiveType::U16 => "u16",
510 PrimitiveType::U32 => "u32",
511 PrimitiveType::U64 => "u64",
512 PrimitiveType::U128 => "u128",
513 PrimitiveType::I8 => "i8",
514 PrimitiveType::I16 => "i16",
515 PrimitiveType::I32 => "i32",
516 PrimitiveType::I64 => "i64",
517 PrimitiveType::I128 => "i128",
518 PrimitiveType::F32 => "f32",
519 PrimitiveType::F64 => "f64",
520 PrimitiveType::Char => "char",
521 PrimitiveType::String => "string",
522 PrimitiveType::Unit => "unit",
523 PrimitiveType::Never => "never",
524 PrimitiveType::Bytes => "bytes",
525 PrimitiveType::Payload => "payload",
526 }
527 }
528}
529
530struct SchemaHasher<'a, Id: Copy> {
535 hasher: blake3::Hasher,
536 resolve: &'a dyn Fn(Id) -> SchemaHash,
537}
538
539impl<'a, Id: Copy> SchemaHasher<'a, Id> {
540 fn new(resolve: &'a dyn Fn(Id) -> SchemaHash) -> Self {
541 Self {
542 hasher: blake3::Hasher::new(),
543 resolve,
544 }
545 }
546
547 fn feed_string(&mut self, s: &str) {
548 self.hasher.update(&(s.len() as u32).to_le_bytes());
549 self.hasher.update(s.as_bytes());
550 }
551
552 fn feed_type_ref(&mut self, tr: &TypeRef<Id>) {
553 match tr {
554 TypeRef::Concrete { type_id, args } => {
555 self.feed_string("concrete");
556 let resolved = (self.resolve)(*type_id);
557 self.hasher.update(&resolved.0.to_le_bytes());
558 if !args.is_empty() {
559 self.feed_string("args");
560 for arg in args {
561 self.feed_type_ref(arg);
562 }
563 }
564 }
565 TypeRef::Var { name } => {
566 self.feed_string("var");
567 self.feed_string(&name.0);
568 }
569 }
570 }
571
572 fn feed_schema(&mut self, kind: &SchemaKind<Id>, type_params: &[TypeParamName]) {
578 match kind {
579 SchemaKind::Primitive { primitive_type } => {
580 self.feed_string(primitive_type.hash_tag());
581 }
582 SchemaKind::Struct { name, fields } => {
583 self.feed_string("struct");
584 self.feed_string(name);
585 self.hasher
586 .update(&(type_params.len() as u32).to_le_bytes());
587 for tp in type_params {
588 self.feed_string(&tp.0);
589 }
590 for field in fields {
591 self.feed_string(&field.name);
592 self.feed_type_ref(&field.type_ref);
593 }
594 }
595 SchemaKind::Enum { name, variants } => {
596 self.feed_string("enum");
597 self.feed_string(name);
598 self.hasher
599 .update(&(type_params.len() as u32).to_le_bytes());
600 for tp in type_params {
601 self.feed_string(&tp.0);
602 }
603 for variant in variants {
604 self.feed_string(&variant.name);
605 self.hasher.update(&variant.index.to_le_bytes());
606 match &variant.payload {
607 VariantPayload::Unit => {
608 self.feed_string("unit");
609 }
610 VariantPayload::Newtype { type_ref } => {
611 self.feed_string("newtype");
612 self.feed_type_ref(type_ref);
613 }
614 VariantPayload::Tuple { types } => {
615 self.feed_string("tuple");
616 for tr in types {
617 self.feed_type_ref(tr);
618 }
619 }
620 VariantPayload::Struct { fields } => {
621 self.feed_string("struct");
622 for field in fields {
623 self.feed_string(&field.name);
624 self.feed_type_ref(&field.type_ref);
625 }
626 }
627 }
628 }
629 }
630 SchemaKind::Tuple { elements } => {
631 self.feed_string("tuple");
632 for elem in elements {
633 self.feed_type_ref(elem);
634 }
635 }
636 SchemaKind::List { element } => {
637 self.feed_string("list");
638 self.feed_type_ref(element);
639 }
640 SchemaKind::Map { key, value } => {
641 self.feed_string("map");
642 self.feed_type_ref(key);
643 self.feed_type_ref(value);
644 }
645 SchemaKind::Array { element, length } => {
646 self.feed_string("array");
647 self.feed_type_ref(element);
648 self.hasher.update(&length.to_le_bytes());
649 }
650 SchemaKind::Option { element } => {
651 self.feed_string("option");
652 self.feed_type_ref(element);
653 }
654 SchemaKind::Channel { direction, element } => {
655 self.feed_string("channel");
656 self.feed_string(match direction {
657 ChannelDirection::Tx => "send",
658 ChannelDirection::Rx => "recv",
659 });
660 self.feed_type_ref(element);
661 }
662 }
663 }
664
665 fn finalize(self) -> SchemaHash {
666 let hash = self.hasher.finalize();
667 let bytes: [u8; 8] = hash.as_bytes()[0..8].try_into().expect("slice len");
668 SchemaHash(u64::from_le_bytes(bytes))
669 }
670}
671
672pub fn compute_content_hash<Id: Copy>(
674 kind: &SchemaKind<Id>,
675 type_params: &[TypeParamName],
676 resolve: &dyn Fn(Id) -> SchemaHash,
677) -> SchemaHash {
678 let mut hasher = SchemaHasher::new(resolve);
679 hasher.feed_schema(kind, type_params);
680 hasher.finalize()
681}
682
683pub fn schema_child_ids(kind: &SchemaKind) -> Vec<SchemaHash> {
685 let mut refs = Vec::new();
686 kind.for_each_type_ref(&mut |tr| tr.collect_ids(&mut refs));
687 refs
688}
689
690#[derive(Facet, Clone, Debug, Default)]
695#[repr(transparent)]
696#[facet(transparent)]
697pub struct CborPayload(pub Vec<u8>);
698
699impl CborPayload {
700 pub fn is_empty(&self) -> bool {
701 self.0.is_empty()
702 }
703}
704
705pub type SchemaRegistry = HashMap<SchemaHash, Schema>;
708
709pub fn build_registry(schemas: &[Schema]) -> SchemaRegistry {
711 schemas.iter().map(|s| (s.id, s.clone())).collect()
712}
713
714pub trait SchemaSource {
720 fn get_schema(&self, id: SchemaHash) -> Option<Schema>;
721}
722
723impl SchemaSource for SchemaRegistry {
724 fn get_schema(&self, id: SchemaHash) -> Option<Schema> {
725 self.get(&id).cloned()
726 }
727}
728
729#[derive(Facet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
731#[repr(u8)]
732#[facet(tag = "tag", rename_all = "snake_case")]
733pub enum BindingDirection {
734 Args,
736
737 Response,
739}
740
741#[derive(Facet, Clone, Debug)]
744pub struct SchemaPayload {
745 pub schemas: Vec<Schema>,
748
749 pub root: TypeRef,
754}
755
756impl SchemaPayload {
757 pub fn to_cbor(&self) -> CborPayload {
759 CborPayload(facet_cbor::to_vec(self).expect("schema CBOR serialization should not fail"))
760 }
761
762 pub fn from_cbor(bytes: &[u8]) -> Result<SchemaPayload, facet_cbor::CborError> {
764 facet_cbor::from_slice(bytes)
765 }
766}
767
768#[repr(transparent)]
771pub struct RawPostcardBorrowed<'a>(pub &'a [u8]);
772
773pub static RAW_POSTCARD_BORROWED_SHAPE: Shape =
776 Shape::builder_for_sized::<RawPostcardBorrowed<'static>>("RawPostcardBorrowed").build();
777
778pub fn opaque_encoded_borrowed(bytes: &&[u8]) -> OpaqueSerialize {
781 OpaqueSerialize {
782 ptr: PtrConst::new((bytes as *const &[u8]).cast::<RawPostcardBorrowed<'_>>()),
783 shape: &RAW_POSTCARD_BORROWED_SHAPE,
784 }
785}