1use std::cmp::Ordering;
7use std::collections::{BTreeMap, BTreeSet};
8use std::io::{self, Write};
9
10use serde::de::{DeserializeSeed, Error as _, IgnoredAny, MapAccess, SeqAccess, Visitor};
11use serde::ser::SerializeMap;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13use serde_json::Value;
14
15use crate::{DependencyClosureIdentityV1, InputIdentity};
16
17pub const CONTACT_FRAGMENT_V1_ID: &str = "urn:animsmith:schema:contact-fragment:1";
19pub const CONTACT_FRAGMENT_V1_SCHEMA_VERSION: u32 = 1;
21pub const CONTACT_FRAGMENT_V1_MAX_SOURCE_BYTES: usize = 8 * 1024 * 1024;
23pub const CONTACT_FRAGMENT_V1_MAX_CANONICAL_BYTES: usize = 8 * 1024 * 1024;
25pub const CONTACT_FRAGMENT_V1_MAX_EVENTS: usize = 4_096;
27pub const CONTACT_FRAGMENT_V1_MAX_EXTENSIONS: usize = 256;
29pub const CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES: usize = 4_096;
31pub const CONTACT_FRAGMENT_V1_MAX_IDENTIFIER_BYTES: usize = 255;
33pub const CONTACT_FRAGMENT_V1_MAX_DEPTH: usize = 32;
35pub const CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES: usize = 256 * 1024;
37pub const CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_DEPTH: usize = 16;
39pub const CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
45const MAX_OPAQUE_MEMBERS: usize = CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES;
48
49#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
51#[non_exhaustive]
52pub enum ContactFragmentError {
53 #[error("contact fragment source has {bytes} bytes, exceeding V1 limit {limit}")]
55 SourceTooLarge {
56 bytes: usize,
58 limit: usize,
60 },
61 #[error("invalid contact fragment JSON: {message}")]
63 InvalidJson {
64 message: String,
66 },
67 #[error("invalid contact fragment {field}: {message}")]
69 InvalidField {
70 field: &'static str,
72 message: String,
74 },
75 #[error("contact fragment {field} has {found}, exceeding V1 limit {limit}")]
77 LimitExceeded {
78 field: &'static str,
80 found: usize,
82 limit: usize,
84 },
85 #[error("contact fragment canonical JSON exceeds V1 limit {limit}")]
87 CanonicalTooLarge {
88 limit: usize,
90 },
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
95#[serde(rename_all = "snake_case")]
96pub enum ContactRoleV1 {
97 LeftFoot,
99 RightFoot,
101 LeftHand,
103 RightHand,
105 LeftToe,
107 RightToe,
109 LeftKnee,
111 RightKnee,
113 LeftElbow,
115 RightElbow,
117 Root,
119 Prop,
121 Body,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
127#[serde(rename_all = "snake_case")]
128pub enum ContactPhaseV1 {
129 Begin,
131 End,
133 Marker,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
139pub struct ContactProducerV1 {
140 tool: String,
141 version: String,
142}
143
144impl ContactProducerV1 {
145 pub fn new(
147 tool: impl Into<String>,
148 version: impl Into<String>,
149 ) -> Result<Self, ContactFragmentError> {
150 let value = Self {
151 tool: tool.into(),
152 version: version.into(),
153 };
154 identifier(&value.tool, "producer.tool")?;
155 identifier(&value.version, "producer.version")?;
156 Ok(value)
157 }
158
159 pub fn tool(&self) -> &str {
161 &self.tool
162 }
163
164 pub fn version(&self) -> &str {
166 &self.version
167 }
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
172#[serde(tag = "scope", rename_all = "snake_case")]
173pub enum ContactClipReferenceV1 {
174 Document {
176 clip_name: String,
178 },
179 Collection {
181 logical_id: String,
183 source: String,
185 take_index: u32,
187 take_name: String,
189 },
190}
191
192impl ContactClipReferenceV1 {
193 pub fn document(clip_name: impl Into<String>) -> Result<Self, ContactFragmentError> {
195 let clip_name = clip_name.into();
196 text(&clip_name, "clip.clip_name")?;
197 Ok(Self::Document { clip_name })
198 }
199
200 pub fn collection(
202 logical_id: impl Into<String>,
203 source: impl Into<String>,
204 take_index: u32,
205 take_name: impl Into<String>,
206 ) -> Result<Self, ContactFragmentError> {
207 let value = Self::Collection {
208 logical_id: logical_id.into(),
209 source: source.into(),
210 take_index,
211 take_name: take_name.into(),
212 };
213 value.validate()?;
214 Ok(value)
215 }
216
217 fn validate(&self) -> Result<(), ContactFragmentError> {
218 match self {
219 Self::Document { clip_name } => text(clip_name, "clip.clip_name"),
220 Self::Collection {
221 logical_id,
222 source,
223 take_name,
224 ..
225 } => {
226 identifier(logical_id, "clip.logical_id")?;
227 identifier(source, "clip.source")?;
228 text(take_name, "clip.take_name")
229 }
230 }
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
236pub struct ContactEventWindowV1 {
237 start: f64,
238 end: f64,
239}
240
241impl ContactEventWindowV1 {
242 pub fn new(start: f64, end: f64) -> Result<Self, ContactFragmentError> {
244 normalized_time(start, "event.window.start")?;
245 normalized_time(end, "event.window.end")?;
246 if start > end {
247 return invalid("event.window", "start must not exceed end");
248 }
249 Ok(Self {
250 start: canonical_number(start),
251 end: canonical_number(end),
252 })
253 }
254 pub const fn start(self) -> f64 {
256 self.start
257 }
258 pub const fn end(self) -> f64 {
260 self.end
261 }
262}
263
264#[derive(Debug, Clone, Copy, PartialEq)]
266pub enum ContactEventKindV1 {
267 Point(f64),
269 Window(ContactEventWindowV1),
271}
272
273#[derive(Debug, Clone, PartialEq)]
275pub struct ContactEventV1 {
276 event_id: String,
277 role: ContactRoleV1,
278 phase: ContactPhaseV1,
279 kind: ContactEventKindV1,
280 confidence: Option<f64>,
281}
282
283impl ContactEventV1 {
284 pub fn point(
286 event_id: impl Into<String>,
287 role: ContactRoleV1,
288 phase: ContactPhaseV1,
289 time: f64,
290 confidence: Option<f64>,
291 ) -> Result<Self, ContactFragmentError> {
292 Self::new(
293 event_id.into(),
294 role,
295 phase,
296 ContactEventKindV1::Point(time),
297 confidence,
298 )
299 }
300 pub fn window(
302 event_id: impl Into<String>,
303 role: ContactRoleV1,
304 phase: ContactPhaseV1,
305 window: ContactEventWindowV1,
306 confidence: Option<f64>,
307 ) -> Result<Self, ContactFragmentError> {
308 Self::new(
309 event_id.into(),
310 role,
311 phase,
312 ContactEventKindV1::Window(window),
313 confidence,
314 )
315 }
316 fn new(
317 event_id: String,
318 role: ContactRoleV1,
319 phase: ContactPhaseV1,
320 kind: ContactEventKindV1,
321 confidence: Option<f64>,
322 ) -> Result<Self, ContactFragmentError> {
323 identifier(&event_id, "event.event_id")?;
324 match kind {
325 ContactEventKindV1::Point(time) => normalized_time(time, "event.time")?,
326 ContactEventKindV1::Window(window) => {
327 let _ = ContactEventWindowV1::new(window.start, window.end)?;
328 }
329 }
330 if let Some(confidence) = confidence {
331 finite_range(confidence, 0.0, 1.0, "event.confidence")?;
332 }
333 let kind = match kind {
334 ContactEventKindV1::Point(time) => ContactEventKindV1::Point(canonical_number(time)),
335 ContactEventKindV1::Window(window) => ContactEventKindV1::Window(window),
336 };
337 Ok(Self {
338 event_id,
339 role,
340 phase,
341 kind,
342 confidence: confidence.map(canonical_number),
343 })
344 }
345 pub fn event_id(&self) -> &str {
347 &self.event_id
348 }
349 pub const fn role(&self) -> ContactRoleV1 {
351 self.role
352 }
353 pub const fn phase(&self) -> ContactPhaseV1 {
355 self.phase
356 }
357 pub const fn kind(&self) -> ContactEventKindV1 {
359 self.kind
360 }
361
362 pub const fn confidence(&self) -> Option<f64> {
364 self.confidence
365 }
366}
367
368impl Serialize for ContactEventV1 {
369 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
370 where
371 S: Serializer,
372 {
373 let mut map = serializer.serialize_map(Some(5))?;
374 map.serialize_entry("event_id", &self.event_id)?;
375 map.serialize_entry("role", &self.role)?;
376 map.serialize_entry("phase", &self.phase)?;
377 match self.kind {
378 ContactEventKindV1::Point(time) => map.serialize_entry("time", &time)?,
379 ContactEventKindV1::Window(window) => map.serialize_entry("window", &window)?,
380 }
381 if let Some(confidence) = self.confidence {
382 map.serialize_entry("confidence", &confidence)?;
383 }
384 map.end()
385 }
386}
387
388#[derive(Debug, Clone, PartialEq, Serialize)]
390pub struct ContactExtensionV1 {
391 schema: String,
392 schema_version: u32,
393 payload: Value,
394}
395
396impl ContactExtensionV1 {
397 pub fn new(
399 schema: impl Into<String>,
400 schema_version: u32,
401 payload: Value,
402 ) -> Result<Self, ContactFragmentError> {
403 let mut value = Self {
404 schema: schema.into(),
405 schema_version,
406 payload,
407 };
408 identifier(&value.schema, "extension.schema")?;
409 if value.schema_version == 0 {
410 return invalid("extension.schema_version", "must be positive");
411 }
412 if !value.payload.is_object() {
413 return invalid("extension.payload", "must be an object");
414 }
415 validate_json_value(
416 &value.payload,
417 1,
418 CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_DEPTH,
419 "extension.payload",
420 )?;
421 let canonical_payload = jcs(
422 &value.payload,
423 CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES,
424 )?;
425 value.payload = serde_json::from_slice(&canonical_payload).map_err(|error| {
426 ContactFragmentError::InvalidField {
427 field: "extension.payload",
428 message: error.to_string(),
429 }
430 })?;
431 Ok(value)
432 }
433
434 pub fn schema(&self) -> &str {
436 &self.schema
437 }
438
439 pub const fn schema_version(&self) -> u32 {
441 self.schema_version
442 }
443
444 pub fn payload(&self) -> &Value {
446 &self.payload
447 }
448}
449
450#[derive(Debug, Clone, PartialEq, Serialize)]
452pub struct ContactFragmentV1 {
453 schema: &'static str,
454 schema_version: u32,
455 producer: ContactProducerV1,
456 artifact: InputIdentity,
457 dependency_closure_identity: DependencyClosureIdentityV1,
458 clip: ContactClipReferenceV1,
459 duration_s: f64,
460 events: Vec<ContactEventV1>,
461 #[serde(skip_serializing_if = "Vec::is_empty")]
462 extensions: Vec<ContactExtensionV1>,
463}
464
465impl ContactFragmentV1 {
466 pub fn new(
468 producer: ContactProducerV1,
469 artifact: InputIdentity,
470 dependency_closure_identity: DependencyClosureIdentityV1,
471 clip: ContactClipReferenceV1,
472 duration_s: f64,
473 mut events: Vec<ContactEventV1>,
474 extensions: Vec<ContactExtensionV1>,
475 ) -> Result<Self, ContactFragmentError> {
476 safe_identity_bytes(&artifact, "artifact.bytes")?;
477 safe_identity_bytes(
478 dependency_closure_identity.input_identity(),
479 "dependency_closure_identity.bytes",
480 )?;
481 if !safe_f64(duration_s) {
482 return invalid("duration_s", "must be an RFC 8785 safe number");
483 }
484 positive_finite(duration_s, "duration_s")?;
485 let duration_s = canonical_number(duration_s);
486 clip.validate()?;
487 if events.len() > CONTACT_FRAGMENT_V1_MAX_EVENTS {
488 return limit("events", events.len(), CONTACT_FRAGMENT_V1_MAX_EVENTS);
489 }
490 if extensions.len() > CONTACT_FRAGMENT_V1_MAX_EXTENSIONS {
491 return limit(
492 "extensions",
493 extensions.len(),
494 CONTACT_FRAGMENT_V1_MAX_EXTENSIONS,
495 );
496 }
497 let mut ids = BTreeSet::new();
498 for event in &events {
499 if !ids.insert(event.event_id.clone()) {
500 return invalid("events", "event_id must be unique");
501 }
502 }
503 events.sort_by(event_order);
504 let result = Self {
505 schema: CONTACT_FRAGMENT_V1_ID,
506 schema_version: CONTACT_FRAGMENT_V1_SCHEMA_VERSION,
507 producer,
508 artifact,
509 dependency_closure_identity,
510 clip,
511 duration_s,
512 events,
513 extensions,
514 };
515 let _ = result.canonical_json()?;
516 Ok(result)
517 }
518
519 pub fn read_json(bytes: &[u8]) -> Result<Self, ContactFragmentError> {
521 if bytes.len() > CONTACT_FRAGMENT_V1_MAX_SOURCE_BYTES {
522 return Err(ContactFragmentError::SourceTooLarge {
523 bytes: bytes.len(),
524 limit: CONTACT_FRAGMENT_V1_MAX_SOURCE_BYTES,
525 });
526 }
527 let mut deserializer = serde_json::Deserializer::from_slice(bytes);
528 let wire = ContactFragmentWire::deserialize(&mut deserializer).map_err(|error| {
529 ContactFragmentError::InvalidJson {
530 message: error.to_string(),
531 }
532 })?;
533 deserializer
534 .end()
535 .map_err(|error| ContactFragmentError::InvalidJson {
536 message: error.to_string(),
537 })?;
538 if wire.events.overflowed {
539 return limit(
540 "events",
541 CONTACT_FRAGMENT_V1_MAX_EVENTS + 1,
542 CONTACT_FRAGMENT_V1_MAX_EVENTS,
543 );
544 }
545 if wire.extensions.overflowed {
546 return limit(
547 "extensions",
548 CONTACT_FRAGMENT_V1_MAX_EXTENSIONS + 1,
549 CONTACT_FRAGMENT_V1_MAX_EXTENSIONS,
550 );
551 }
552 if wire.extensions_present && wire.extensions.values.is_empty() {
553 return invalid("extensions", "must be omitted when empty");
554 }
555 let value = wire.into_value();
556 parse_fragment(value)
557 }
558
559 pub fn canonical_json(&self) -> Result<Vec<u8>, ContactFragmentError> {
561 jcs(self, CONTACT_FRAGMENT_V1_MAX_CANONICAL_BYTES)
562 }
563 pub fn canonical_identity(&self) -> Result<InputIdentity, ContactFragmentError> {
565 Ok(InputIdentity::from_bytes(&self.canonical_json()?))
566 }
567
568 pub fn producer(&self) -> &ContactProducerV1 {
570 &self.producer
571 }
572
573 pub fn events(&self) -> &[ContactEventV1] {
575 &self.events
576 }
577
578 pub fn artifact(&self) -> &InputIdentity {
580 &self.artifact
581 }
582
583 pub fn dependency_closure_identity(&self) -> &DependencyClosureIdentityV1 {
585 &self.dependency_closure_identity
586 }
587
588 pub fn clip(&self) -> &ContactClipReferenceV1 {
590 &self.clip
591 }
592
593 pub const fn duration_s(&self) -> f64 {
595 self.duration_s
596 }
597
598 pub fn extensions(&self) -> &[ContactExtensionV1] {
600 &self.extensions
601 }
602}
603
604fn parse_fragment(value: Value) -> Result<ContactFragmentV1, ContactFragmentError> {
605 let mut root = object(value, "fragment")?;
606 exact_fields(
607 &root,
608 "fragment",
609 &[
610 "schema",
611 "schema_version",
612 "producer",
613 "artifact",
614 "dependency_closure_identity",
615 "clip",
616 "duration_s",
617 "events",
618 "extensions",
619 ],
620 )?;
621 let schema = string(take(&mut root, "schema", "fragment")?, "schema")?;
622 if schema != CONTACT_FRAGMENT_V1_ID {
623 return invalid("schema", "must equal contact-fragment V1 schema id");
624 }
625 let version = u32_value(
626 take(&mut root, "schema_version", "fragment")?,
627 "schema_version",
628 )?;
629 if version != CONTACT_FRAGMENT_V1_SCHEMA_VERSION {
630 return invalid("schema_version", "must equal 1");
631 }
632 let producer = parse_producer(take(&mut root, "producer", "fragment")?)?;
633 let artifact = parse_input_identity(take(&mut root, "artifact", "fragment")?, "artifact")?;
634 let dependency_closure_identity = parse_dependency_closure_identity(
635 take(&mut root, "dependency_closure_identity", "fragment")?,
636 "dependency_closure_identity",
637 )?;
638 let clip = parse_clip(take(&mut root, "clip", "fragment")?)?;
639 let duration_s = number(take(&mut root, "duration_s", "fragment")?, "duration_s")?;
640 let events = array(take(&mut root, "events", "fragment")?, "events")?
641 .into_iter()
642 .map(parse_event)
643 .collect::<Result<Vec<_>, _>>()?;
644 let extensions = match root.remove("extensions") {
645 Some(value) => array(value, "extensions")?
646 .into_iter()
647 .map(parse_extension)
648 .collect::<Result<Vec<_>, _>>()?,
649 None => Vec::new(),
650 };
651 ContactFragmentV1::new(
652 producer,
653 artifact,
654 dependency_closure_identity,
655 clip,
656 duration_s,
657 events,
658 extensions,
659 )
660}
661
662fn parse_producer(value: Value) -> Result<ContactProducerV1, ContactFragmentError> {
663 let mut value = object(value, "producer")?;
664 exact_fields(&value, "producer", &["tool", "version"])?;
665 ContactProducerV1::new(
666 string(take(&mut value, "tool", "producer")?, "producer.tool")?,
667 string(take(&mut value, "version", "producer")?, "producer.version")?,
668 )
669}
670
671fn parse_input_identity(
672 value: Value,
673 field: &'static str,
674) -> Result<InputIdentity, ContactFragmentError> {
675 serde_json::from_value(normalize_identity_wire(value, field)?).map_err(|error| {
676 ContactFragmentError::InvalidField {
677 field,
678 message: error.to_string(),
679 }
680 })
681}
682
683fn parse_dependency_closure_identity(
684 value: Value,
685 field: &'static str,
686) -> Result<DependencyClosureIdentityV1, ContactFragmentError> {
687 serde_json::from_value(normalize_identity_wire(value, field)?).map_err(|error| {
688 ContactFragmentError::InvalidField {
689 field,
690 message: error.to_string(),
691 }
692 })
693}
694
695fn normalize_identity_wire(
696 value: Value,
697 field: &'static str,
698) -> Result<Value, ContactFragmentError> {
699 let mut value = object(value, field)?;
700 let bytes_field = if field == "artifact" {
701 "artifact.bytes"
702 } else {
703 "dependency_closure_identity.bytes"
704 };
705 let bytes = unsigned_integer(
706 take(&mut value, "bytes", field)?,
707 CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER,
708 bytes_field,
709 )?;
710 value.insert("bytes".into(), Value::Number(bytes.into()));
711 Ok(Value::Object(value))
712}
713fn parse_clip(value: Value) -> Result<ContactClipReferenceV1, ContactFragmentError> {
714 let mut value = object(value, "clip")?;
715 let scope = string(take(&mut value, "scope", "clip")?, "clip.scope")?;
716 match scope.as_str() {
717 "document" => {
718 exact_fields(&value, "clip", &["scope", "clip_name"])?;
719 ContactClipReferenceV1::document(string(
720 take(&mut value, "clip_name", "clip")?,
721 "clip.clip_name",
722 )?)
723 }
724 "collection" => {
725 exact_fields(
726 &value,
727 "clip",
728 &["scope", "logical_id", "source", "take_index", "take_name"],
729 )?;
730 ContactClipReferenceV1::collection(
731 string(take(&mut value, "logical_id", "clip")?, "clip.logical_id")?,
732 string(take(&mut value, "source", "clip")?, "clip.source")?,
733 u32_value(take(&mut value, "take_index", "clip")?, "clip.take_index")?,
734 string(take(&mut value, "take_name", "clip")?, "clip.take_name")?,
735 )
736 }
737 _ => invalid("clip.scope", "must be document or collection"),
738 }
739}
740fn parse_event(value: Value) -> Result<ContactEventV1, ContactFragmentError> {
741 let mut value = object(value, "event")?;
742 exact_fields(
743 &value,
744 "event",
745 &["event_id", "role", "phase", "time", "window", "confidence"],
746 )?;
747 let event_id = string(take(&mut value, "event_id", "event")?, "event.event_id")?;
748 let role = parse_role(&string(take(&mut value, "role", "event")?, "event.role")?)?;
749 let phase = parse_phase(&string(take(&mut value, "phase", "event")?, "event.phase")?)?;
750 let confidence = value
751 .remove("confidence")
752 .map(|value| number(value, "event.confidence"))
753 .transpose()?;
754 match (value.remove("time"), value.remove("window")) {
755 (Some(time), None) => ContactEventV1::point(
756 event_id,
757 role,
758 phase,
759 number(time, "event.time")?,
760 confidence,
761 ),
762 (None, Some(window)) => {
763 let mut window = object(window, "event.window")?;
764 exact_fields(&window, "event.window", &["start", "end"])?;
765 ContactEventV1::window(
766 event_id,
767 role,
768 phase,
769 ContactEventWindowV1::new(
770 number(
771 take(&mut window, "start", "event.window")?,
772 "event.window.start",
773 )?,
774 number(
775 take(&mut window, "end", "event.window")?,
776 "event.window.end",
777 )?,
778 )?,
779 confidence,
780 )
781 }
782 _ => invalid("event", "must contain exactly one of time or window"),
783 }
784}
785fn parse_extension(value: Value) -> Result<ContactExtensionV1, ContactFragmentError> {
786 let mut value = object(value, "extension")?;
787 exact_fields(
788 &value,
789 "extension",
790 &["schema", "schema_version", "payload"],
791 )?;
792 ContactExtensionV1::new(
793 string(take(&mut value, "schema", "extension")?, "extension.schema")?,
794 u32_value(
795 take(&mut value, "schema_version", "extension")?,
796 "extension.schema_version",
797 )?,
798 take(&mut value, "payload", "extension")?,
799 )
800}
801
802fn parse_role(value: &str) -> Result<ContactRoleV1, ContactFragmentError> {
803 Ok(match value {
804 "left_foot" => ContactRoleV1::LeftFoot,
805 "right_foot" => ContactRoleV1::RightFoot,
806 "left_hand" => ContactRoleV1::LeftHand,
807 "right_hand" => ContactRoleV1::RightHand,
808 "left_toe" => ContactRoleV1::LeftToe,
809 "right_toe" => ContactRoleV1::RightToe,
810 "left_knee" => ContactRoleV1::LeftKnee,
811 "right_knee" => ContactRoleV1::RightKnee,
812 "left_elbow" => ContactRoleV1::LeftElbow,
813 "right_elbow" => ContactRoleV1::RightElbow,
814 "root" => ContactRoleV1::Root,
815 "prop" => ContactRoleV1::Prop,
816 "body" => ContactRoleV1::Body,
817 _ => return invalid("event.role", "is not a V1 role"),
818 })
819}
820fn parse_phase(value: &str) -> Result<ContactPhaseV1, ContactFragmentError> {
821 Ok(match value {
822 "begin" => ContactPhaseV1::Begin,
823 "end" => ContactPhaseV1::End,
824 "marker" => ContactPhaseV1::Marker,
825 _ => return invalid("event.phase", "is not a V1 phase"),
826 })
827}
828
829fn event_order(left: &ContactEventV1, right: &ContactEventV1) -> Ordering {
830 let (left_start, left_rank, left_end) = event_key(left);
831 let (right_start, right_rank, right_end) = event_key(right);
832 left_start
833 .total_cmp(&right_start)
834 .then(left_rank.cmp(&right_rank))
835 .then_with(|| match (left_end, right_end) {
836 (None, None) => Ordering::Equal,
837 (None, Some(_)) => Ordering::Less,
838 (Some(_), None) => Ordering::Greater,
839 (Some(a), Some(b)) => a.total_cmp(&b),
840 })
841 .then(utf16_cmp(role_name(left.role), role_name(right.role)))
842 .then(utf16_cmp(phase_name(left.phase), phase_name(right.phase)))
843 .then(utf16_cmp(&left.event_id, &right.event_id))
844}
845fn event_key(event: &ContactEventV1) -> (f64, u8, Option<f64>) {
846 match event.kind {
847 ContactEventKindV1::Point(time) => (time, 0, None),
848 ContactEventKindV1::Window(window) => (window.start, 1, Some(window.end)),
849 }
850}
851fn utf16_cmp(left: &str, right: &str) -> Ordering {
852 left.encode_utf16().cmp(right.encode_utf16())
853}
854fn role_name(value: ContactRoleV1) -> &'static str {
855 match value {
856 ContactRoleV1::LeftFoot => "left_foot",
857 ContactRoleV1::RightFoot => "right_foot",
858 ContactRoleV1::LeftHand => "left_hand",
859 ContactRoleV1::RightHand => "right_hand",
860 ContactRoleV1::LeftToe => "left_toe",
861 ContactRoleV1::RightToe => "right_toe",
862 ContactRoleV1::LeftKnee => "left_knee",
863 ContactRoleV1::RightKnee => "right_knee",
864 ContactRoleV1::LeftElbow => "left_elbow",
865 ContactRoleV1::RightElbow => "right_elbow",
866 ContactRoleV1::Root => "root",
867 ContactRoleV1::Prop => "prop",
868 ContactRoleV1::Body => "body",
869 }
870}
871fn phase_name(value: ContactPhaseV1) -> &'static str {
872 match value {
873 ContactPhaseV1::Begin => "begin",
874 ContactPhaseV1::End => "end",
875 ContactPhaseV1::Marker => "marker",
876 }
877}
878
879fn object(
880 value: Value,
881 field: &'static str,
882) -> Result<serde_json::Map<String, Value>, ContactFragmentError> {
883 match value {
884 Value::Object(value) => Ok(value),
885 _ => Err(ContactFragmentError::InvalidField {
886 field,
887 message: "must be an object".into(),
888 }),
889 }
890}
891fn array(value: Value, field: &'static str) -> Result<Vec<Value>, ContactFragmentError> {
892 match value {
893 Value::Array(value) => Ok(value),
894 _ => Err(ContactFragmentError::InvalidField {
895 field,
896 message: "must be an array".into(),
897 }),
898 }
899}
900fn string(value: Value, field: &'static str) -> Result<String, ContactFragmentError> {
901 value
902 .as_str()
903 .map(str::to_owned)
904 .ok_or_else(|| ContactFragmentError::InvalidField {
905 field,
906 message: "must be a string".into(),
907 })
908}
909fn number(value: Value, field: &'static str) -> Result<f64, ContactFragmentError> {
910 value
911 .as_f64()
912 .filter(|value| value.is_finite())
913 .ok_or_else(|| ContactFragmentError::InvalidField {
914 field,
915 message: "must be a finite JSON number".into(),
916 })
917}
918fn u32_value(value: Value, field: &'static str) -> Result<u32, ContactFragmentError> {
919 Ok(unsigned_integer(value, u64::from(u32::MAX), field)? as u32)
920}
921
922fn unsigned_integer(
923 value: Value,
924 maximum: u64,
925 field: &'static str,
926) -> Result<u64, ContactFragmentError> {
927 let Value::Number(number) = value else {
928 return invalid(field, "must be a nonnegative integer-valued JSON number");
929 };
930 if let Some(value) = number.as_u64() {
931 if value <= maximum {
932 return Ok(value);
933 }
934 } else if let Some(value) = number.as_f64()
935 && safe_f64(value)
936 && value >= 0.0
937 && value.fract() == 0.0
938 && value <= maximum as f64
939 {
940 return Ok(value as u64);
941 }
942 invalid(field, "must be a nonnegative integer-valued JSON number")
943}
944fn take(
945 map: &mut serde_json::Map<String, Value>,
946 key: &'static str,
947 field: &'static str,
948) -> Result<Value, ContactFragmentError> {
949 map.remove(key)
950 .ok_or_else(|| ContactFragmentError::InvalidField {
951 field,
952 message: format!("is missing {key:?}"),
953 })
954}
955fn exact_fields(
956 map: &serde_json::Map<String, Value>,
957 field: &'static str,
958 allowed: &[&str],
959) -> Result<(), ContactFragmentError> {
960 for key in map.keys() {
961 if !allowed.contains(&key.as_str()) {
962 return invalid(field, "contains an unknown field");
963 }
964 }
965 Ok(())
966}
967fn invalid<T>(field: &'static str, message: &'static str) -> Result<T, ContactFragmentError> {
968 Err(ContactFragmentError::InvalidField {
969 field,
970 message: message.into(),
971 })
972}
973fn limit<T>(field: &'static str, found: usize, limit: usize) -> Result<T, ContactFragmentError> {
974 Err(ContactFragmentError::LimitExceeded {
975 field,
976 found,
977 limit,
978 })
979}
980fn text(value: &str, field: &'static str) -> Result<(), ContactFragmentError> {
981 if value.is_empty() {
982 return invalid(field, "must not be empty");
983 }
984 if value.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
985 return limit(field, value.len(), CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES);
986 }
987 Ok(())
988}
989fn identifier(value: &str, field: &'static str) -> Result<(), ContactFragmentError> {
990 text(value, field)?;
991 if value.len() > CONTACT_FRAGMENT_V1_MAX_IDENTIFIER_BYTES {
992 return limit(field, value.len(), CONTACT_FRAGMENT_V1_MAX_IDENTIFIER_BYTES);
993 }
994 Ok(())
995}
996fn positive_finite(value: f64, field: &'static str) -> Result<(), ContactFragmentError> {
997 if !value.is_finite() || value <= 0.0 {
998 return invalid(field, "must be finite and positive");
999 }
1000 Ok(())
1001}
1002fn normalized_time(value: f64, field: &'static str) -> Result<(), ContactFragmentError> {
1003 finite_range(value, 0.0, 1.0, field)
1004}
1005fn finite_range(
1006 value: f64,
1007 min: f64,
1008 max: f64,
1009 field: &'static str,
1010) -> Result<(), ContactFragmentError> {
1011 if !value.is_finite() || value < min || value > max {
1012 return invalid(field, "must be finite and within its V1 range");
1013 }
1014 Ok(())
1015}
1016fn canonical_number(value: f64) -> f64 {
1017 if value == 0.0 { 0.0 } else { value }
1018}
1019
1020fn safe_identity_bytes(
1021 identity: &InputIdentity,
1022 field: &'static str,
1023) -> Result<(), ContactFragmentError> {
1024 if identity.bytes() > CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER {
1025 return invalid(field, "must be an RFC 8785 safe integer");
1026 }
1027 Ok(())
1028}
1029
1030fn safe_i64(value: i64) -> bool {
1031 value.unsigned_abs() <= CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER
1032}
1033
1034fn safe_u64(value: u64) -> bool {
1035 value <= CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER
1036}
1037
1038fn safe_f64(value: f64) -> bool {
1039 value.is_finite() && value.abs() <= CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER as f64
1040}
1041
1042fn validate_jcs_number(
1043 value: &serde_json::Number,
1044 field: &'static str,
1045) -> Result<(), ContactFragmentError> {
1046 if let Some(value) = value.as_i64() {
1047 if !safe_i64(value) {
1048 return invalid(field, "contains an RFC 8785 unsafe integer");
1049 }
1050 } else if let Some(value) = value.as_u64() {
1051 if !safe_u64(value) {
1052 return invalid(field, "contains an RFC 8785 unsafe integer");
1053 }
1054 } else if !value.as_f64().is_some_and(safe_f64) {
1055 return invalid(field, "contains a non-finite or RFC 8785 unsafe number");
1056 }
1057 Ok(())
1058}
1059
1060fn validate_json_value(
1061 value: &Value,
1062 depth: usize,
1063 max_depth: usize,
1064 field: &'static str,
1065) -> Result<(), ContactFragmentError> {
1066 if matches!(value, Value::Array(_) | Value::Object(_)) && depth > max_depth {
1067 return limit(field, depth, max_depth);
1068 }
1069 match value {
1070 Value::String(value) => {
1071 if value.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1072 return limit(field, value.len(), CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES);
1073 }
1074 }
1075 Value::Array(values) => {
1076 for value in values {
1077 validate_json_value(value, depth + 1, max_depth, field)?;
1078 }
1079 }
1080 Value::Object(values) => {
1081 for (key, value) in values {
1082 if key.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1083 return limit(field, key.len(), CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES);
1084 }
1085 validate_json_value(value, depth + 1, max_depth, field)?;
1086 }
1087 }
1088 Value::Number(value) => validate_jcs_number(value, field)?,
1089 _ => {}
1090 }
1091 Ok(())
1092}
1093
1094fn jcs<T: Serialize>(value: &T, limit: usize) -> Result<Vec<u8>, ContactFragmentError> {
1095 let mut output = CappedWriter {
1096 bytes: Vec::new(),
1097 limit,
1098 overflowed: false,
1099 };
1100 serde_jcs::to_writer(&mut output, value).map_err(|error| {
1101 if output.overflowed {
1102 ContactFragmentError::CanonicalTooLarge { limit }
1103 } else {
1104 ContactFragmentError::InvalidJson {
1105 message: error.to_string(),
1106 }
1107 }
1108 })?;
1109 Ok(output.bytes)
1110}
1111struct CappedWriter {
1112 bytes: Vec<u8>,
1113 limit: usize,
1114 overflowed: bool,
1115}
1116impl Write for CappedWriter {
1117 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
1118 if self.bytes.len().saturating_add(bytes.len()) > self.limit {
1119 self.overflowed = true;
1120 return Err(io::Error::other("contact-fragment canonical limit"));
1121 }
1122 self.bytes.extend_from_slice(bytes);
1123 Ok(bytes.len())
1124 }
1125 fn flush(&mut self) -> io::Result<()> {
1126 Ok(())
1127 }
1128}
1129
1130enum StrictJsonValue {
1131 Null,
1132 Bool(bool),
1133 Number(serde_json::Number),
1134 String(String),
1135 Array(Vec<Self>),
1136 Object(BTreeMap<String, Self>),
1137}
1138
1139struct CappedValues {
1140 values: Vec<StrictJsonValue>,
1141 overflowed: bool,
1142}
1143
1144struct CappedValuesSeed {
1145 limit: usize,
1146 depth: usize,
1147}
1148
1149struct CappedExtensionValuesSeed;
1150
1151impl<'de> DeserializeSeed<'de> for CappedExtensionValuesSeed {
1152 type Value = CappedValues;
1153
1154 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1155 where
1156 D: Deserializer<'de>,
1157 {
1158 struct CappedExtensionValuesVisitor;
1159
1160 impl<'de> Visitor<'de> for CappedExtensionValuesVisitor {
1161 type Value = CappedValues;
1162
1163 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1164 formatter.write_str("a JSON array with bounded contact extensions")
1165 }
1166
1167 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
1168 where
1169 A: SeqAccess<'de>,
1170 {
1171 let mut values = Vec::with_capacity(
1172 sequence
1173 .size_hint()
1174 .unwrap_or(0)
1175 .min(CONTACT_FRAGMENT_V1_MAX_EXTENSIONS),
1176 );
1177 while values.len() < CONTACT_FRAGMENT_V1_MAX_EXTENSIONS {
1178 let Some(value) = sequence.next_element::<ContactExtensionWire>()? else {
1179 return Ok(CappedValues {
1180 values,
1181 overflowed: false,
1182 });
1183 };
1184 values.push(value.value);
1185 }
1186 let overflowed = sequence.next_element::<IgnoredAny>()?.is_some();
1187 if overflowed {
1188 while sequence.next_element::<IgnoredAny>()?.is_some() {}
1189 }
1190 Ok(CappedValues { values, overflowed })
1191 }
1192 }
1193
1194 deserializer.deserialize_seq(CappedExtensionValuesVisitor)
1195 }
1196}
1197
1198struct ContactExtensionWire {
1199 value: StrictJsonValue,
1200}
1201
1202impl<'de> Deserialize<'de> for ContactExtensionWire {
1203 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1204 where
1205 D: Deserializer<'de>,
1206 {
1207 struct ContactExtensionVisitor;
1208
1209 impl<'de> Visitor<'de> for ContactExtensionVisitor {
1210 type Value = ContactExtensionWire;
1211
1212 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1213 formatter.write_str("a strict bounded contact extension object")
1214 }
1215
1216 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1217 where
1218 A: MapAccess<'de>,
1219 {
1220 let mut fields = BTreeMap::new();
1221 while let Some(key) = map.next_key::<String>()? {
1222 if fields.contains_key(&key) {
1223 return Err(A::Error::custom(format!("duplicate object member {key:?}")));
1224 }
1225 let value = match key.as_str() {
1226 "schema" | "schema_version" => {
1227 map.next_value_seed(StrictJsonValueSeed { depth: 4 })?
1228 }
1229 "payload" => {
1230 map.next_value_seed(MeasuredJsonValueSeed { depth: 1 })?
1231 .value
1232 }
1233 _ => {
1234 let _ = map.next_value::<IgnoredAny>()?;
1235 return Err(A::Error::custom(
1236 "contact extension contains an unknown field",
1237 ));
1238 }
1239 };
1240 fields.insert(key, value);
1241 }
1242 Ok(ContactExtensionWire {
1243 value: StrictJsonValue::Object(fields),
1244 })
1245 }
1246 }
1247
1248 deserializer.deserialize_map(ContactExtensionVisitor)
1249 }
1250}
1251
1252impl<'de> DeserializeSeed<'de> for CappedValuesSeed {
1253 type Value = CappedValues;
1254
1255 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1256 where
1257 D: Deserializer<'de>,
1258 {
1259 struct CappedValuesVisitor {
1260 limit: usize,
1261 depth: usize,
1262 }
1263
1264 impl<'de> Visitor<'de> for CappedValuesVisitor {
1265 type Value = CappedValues;
1266
1267 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1268 write!(formatter, "a JSON array with at most {} values", self.limit)
1269 }
1270
1271 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
1272 where
1273 A: SeqAccess<'de>,
1274 {
1275 let mut values =
1276 Vec::with_capacity(sequence.size_hint().unwrap_or(0).min(self.limit));
1277 while values.len() < self.limit {
1278 let Some(value) =
1279 sequence.next_element_seed(StrictJsonValueSeed { depth: self.depth })?
1280 else {
1281 return Ok(CappedValues {
1282 values,
1283 overflowed: false,
1284 });
1285 };
1286 values.push(value);
1287 }
1288 let overflowed = sequence.next_element::<IgnoredAny>()?.is_some();
1289 if overflowed {
1290 while sequence.next_element::<IgnoredAny>()?.is_some() {}
1291 }
1292 Ok(CappedValues { values, overflowed })
1293 }
1294 }
1295
1296 deserializer.deserialize_seq(CappedValuesVisitor {
1297 limit: self.limit,
1298 depth: self.depth,
1299 })
1300 }
1301}
1302
1303struct ContactFragmentWire {
1304 fields: BTreeMap<String, StrictJsonValue>,
1305 events: CappedValues,
1306 events_present: bool,
1307 extensions: CappedValues,
1308 extensions_present: bool,
1309}
1310
1311impl ContactFragmentWire {
1312 fn into_value(self) -> Value {
1313 let mut fields = self
1314 .fields
1315 .into_iter()
1316 .map(|(key, value)| (key, value.into_value()))
1317 .collect::<serde_json::Map<_, _>>();
1318 if self.events_present {
1319 fields.insert(
1320 "events".into(),
1321 Value::Array(
1322 self.events
1323 .values
1324 .into_iter()
1325 .map(StrictJsonValue::into_value)
1326 .collect(),
1327 ),
1328 );
1329 }
1330 if !self.extensions.values.is_empty() {
1331 fields.insert(
1332 "extensions".into(),
1333 Value::Array(
1334 self.extensions
1335 .values
1336 .into_iter()
1337 .map(StrictJsonValue::into_value)
1338 .collect(),
1339 ),
1340 );
1341 }
1342 Value::Object(fields)
1343 }
1344}
1345
1346impl<'de> Deserialize<'de> for ContactFragmentWire {
1347 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1348 where
1349 D: Deserializer<'de>,
1350 {
1351 struct ContactFragmentVisitor;
1352
1353 impl<'de> Visitor<'de> for ContactFragmentVisitor {
1354 type Value = ContactFragmentWire;
1355
1356 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1357 formatter.write_str("a strict contact-fragment V1 object")
1358 }
1359
1360 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
1361 where
1362 A: MapAccess<'de>,
1363 {
1364 let mut fields = BTreeMap::new();
1365 let mut seen = BTreeSet::new();
1366 let mut events = None;
1367 let mut extensions = None;
1368 while let Some(key) = map.next_key::<String>()? {
1369 if !seen.insert(key.clone()) {
1370 return Err(A::Error::custom(format!("duplicate object member {key:?}")));
1371 }
1372 match key.as_str() {
1373 "events" => {
1374 events = Some(map.next_value_seed(CappedValuesSeed {
1375 limit: CONTACT_FRAGMENT_V1_MAX_EVENTS,
1376 depth: 3,
1377 })?)
1378 }
1379 "extensions" => {
1380 extensions = Some(map.next_value_seed(CappedExtensionValuesSeed)?)
1381 }
1382 "schema"
1383 | "schema_version"
1384 | "producer"
1385 | "artifact"
1386 | "dependency_closure_identity"
1387 | "clip"
1388 | "duration_s" => {
1389 fields.insert(
1390 key,
1391 map.next_value_seed(StrictJsonValueSeed { depth: 2 })?,
1392 );
1393 }
1394 _ => {
1395 let _ = map.next_value::<IgnoredAny>()?;
1396 return Err(A::Error::custom(
1397 "contact fragment contains an unknown field",
1398 ));
1399 }
1400 }
1401 }
1402 let extensions_present = extensions.is_some();
1403 Ok(ContactFragmentWire {
1404 fields,
1405 events_present: events.is_some(),
1406 events: events.unwrap_or(CappedValues {
1407 values: Vec::new(),
1408 overflowed: false,
1409 }),
1410 extensions: extensions.unwrap_or(CappedValues {
1411 values: Vec::new(),
1412 overflowed: false,
1413 }),
1414 extensions_present,
1415 })
1416 }
1417 }
1418
1419 deserializer.deserialize_map(ContactFragmentVisitor)
1420 }
1421}
1422
1423struct StrictJsonValueSeed {
1424 depth: usize,
1425}
1426
1427impl<'de> DeserializeSeed<'de> for StrictJsonValueSeed {
1428 type Value = StrictJsonValue;
1429
1430 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1431 where
1432 D: Deserializer<'de>,
1433 {
1434 deserializer.deserialize_any(StrictJsonValueVisitor { depth: self.depth })
1435 }
1436}
1437
1438struct StrictJsonValueVisitor {
1439 depth: usize,
1440}
1441impl StrictJsonValue {
1442 fn into_value(self) -> Value {
1443 match self {
1444 Self::Null => Value::Null,
1445 Self::Bool(value) => Value::Bool(value),
1446 Self::Number(value) => Value::Number(value),
1447 Self::String(value) => Value::String(value),
1448 Self::Array(values) => Value::Array(values.into_iter().map(Self::into_value).collect()),
1449 Self::Object(values) => Value::Object(
1450 values
1451 .into_iter()
1452 .map(|(key, value)| (key, value.into_value()))
1453 .collect(),
1454 ),
1455 }
1456 }
1457}
1458impl<'de> Deserialize<'de> for StrictJsonValue {
1459 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1460 where
1461 D: Deserializer<'de>,
1462 {
1463 StrictJsonValueSeed { depth: 1 }.deserialize(deserializer)
1464 }
1465}
1466
1467impl<'de> Visitor<'de> for StrictJsonValueVisitor {
1468 type Value = StrictJsonValue;
1469 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1470 f.write_str("JSON value")
1471 }
1472 fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
1473 Ok(StrictJsonValue::Null)
1474 }
1475 fn visit_bool<E: serde::de::Error>(self, value: bool) -> Result<Self::Value, E> {
1476 Ok(StrictJsonValue::Bool(value))
1477 }
1478 fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Self::Value, E> {
1479 if !safe_i64(value) {
1480 return Err(E::custom(
1481 "contact fragment contains an RFC 8785 unsafe integer",
1482 ));
1483 }
1484 Ok(StrictJsonValue::Number(value.into()))
1485 }
1486 fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Self::Value, E> {
1487 if !safe_u64(value) {
1488 return Err(E::custom(
1489 "contact fragment contains an RFC 8785 unsafe integer",
1490 ));
1491 }
1492 Ok(StrictJsonValue::Number(value.into()))
1493 }
1494 fn visit_f64<E: serde::de::Error>(self, value: f64) -> Result<Self::Value, E> {
1495 if !safe_f64(value) {
1496 return Err(E::custom(
1497 "contact fragment contains a non-finite or RFC 8785 unsafe number",
1498 ));
1499 }
1500 serde_json::Number::from_f64(value)
1501 .map(StrictJsonValue::Number)
1502 .ok_or_else(|| E::custom("non-finite number"))
1503 }
1504 fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
1505 if value.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1506 return Err(E::custom("contact fragment string exceeds V1 byte limit"));
1507 }
1508 Ok(StrictJsonValue::String(value.into()))
1509 }
1510 fn visit_string<E: serde::de::Error>(self, value: String) -> Result<Self::Value, E> {
1511 if value.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1512 return Err(E::custom("contact fragment string exceeds V1 byte limit"));
1513 }
1514 Ok(StrictJsonValue::String(value))
1515 }
1516 fn visit_seq<A: SeqAccess<'de>>(self, mut values: A) -> Result<Self::Value, A::Error> {
1517 if self.depth > CONTACT_FRAGMENT_V1_MAX_DEPTH {
1518 return Err(A::Error::custom(
1519 "contact fragment JSON exceeds V1 nesting depth",
1520 ));
1521 }
1522 let mut output =
1523 Vec::with_capacity(values.size_hint().unwrap_or(0).min(MAX_OPAQUE_MEMBERS));
1524 while output.len() < MAX_OPAQUE_MEMBERS {
1525 let Some(value) = values.next_element_seed(StrictJsonValueSeed {
1526 depth: self.depth + 1,
1527 })?
1528 else {
1529 return Ok(StrictJsonValue::Array(output));
1530 };
1531 output.push(value);
1532 }
1533 let _ = values.next_element::<IgnoredAny>()?;
1534 Err(A::Error::custom(
1535 "contact fragment nested array exceeds V1 bounded member limit",
1536 ))
1537 }
1538 fn visit_map<A: MapAccess<'de>>(self, mut values: A) -> Result<Self::Value, A::Error> {
1539 if self.depth > CONTACT_FRAGMENT_V1_MAX_DEPTH {
1540 return Err(A::Error::custom(
1541 "contact fragment JSON exceeds V1 nesting depth",
1542 ));
1543 }
1544 let mut output = BTreeMap::new();
1545 while output.len() < MAX_OPAQUE_MEMBERS {
1546 let Some(key) = values.next_key::<String>()? else {
1547 return Ok(StrictJsonValue::Object(output));
1548 };
1549 if key.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1550 return Err(A::Error::custom(
1551 "contact fragment object key exceeds V1 byte limit",
1552 ));
1553 }
1554 let value = values.next_value_seed(StrictJsonValueSeed {
1555 depth: self.depth + 1,
1556 })?;
1557 if output.insert(key.clone(), value).is_some() {
1558 return Err(A::Error::custom(format!("duplicate object member {key:?}")));
1559 }
1560 }
1561 let _ = values.next_key::<IgnoredAny>()?;
1562 Err(A::Error::custom(
1563 "contact fragment nested object exceeds V1 bounded member limit",
1564 ))
1565 }
1566}
1567
1568struct MeasuredJsonValue {
1572 value: StrictJsonValue,
1573 canonical_len: usize,
1574}
1575
1576struct MeasuredJsonValueSeed {
1577 depth: usize,
1578}
1579
1580impl<'de> DeserializeSeed<'de> for MeasuredJsonValueSeed {
1581 type Value = MeasuredJsonValue;
1582
1583 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
1584 where
1585 D: Deserializer<'de>,
1586 {
1587 deserializer.deserialize_any(MeasuredJsonValueVisitor { depth: self.depth })
1588 }
1589}
1590
1591struct MeasuredJsonValueVisitor {
1592 depth: usize,
1593}
1594
1595impl MeasuredJsonValue {
1596 fn scalar<E: serde::de::Error>(value: StrictJsonValue) -> Result<Self, E> {
1597 let json = match &value {
1598 StrictJsonValue::Null => Value::Null,
1599 StrictJsonValue::Bool(value) => Value::Bool(*value),
1600 StrictJsonValue::Number(value) => Value::Number(value.clone()),
1601 StrictJsonValue::String(value) => Value::String(value.clone()),
1602 StrictJsonValue::Array(_) | StrictJsonValue::Object(_) => {
1603 unreachable!("scalar measurement only receives scalars")
1604 }
1605 };
1606 let canonical_len = serde_jcs::to_vec(&json).map_err(E::custom)?.len();
1607 if canonical_len > CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES {
1608 return Err(E::custom(
1609 "contact extension payload exceeds V1 canonical byte limit",
1610 ));
1611 }
1612 Ok(Self {
1613 value,
1614 canonical_len,
1615 })
1616 }
1617
1618 fn bounded<E: serde::de::Error>(canonical_len: usize) -> Result<(), E> {
1619 if canonical_len > CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES {
1620 return Err(E::custom(
1621 "contact extension payload exceeds V1 canonical byte limit",
1622 ));
1623 }
1624 Ok(())
1625 }
1626}
1627
1628impl<'de> Visitor<'de> for MeasuredJsonValueVisitor {
1629 type Value = MeasuredJsonValue;
1630
1631 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1632 formatter.write_str("a bounded JSON extension payload value")
1633 }
1634
1635 fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
1636 MeasuredJsonValue::scalar(StrictJsonValue::Null)
1637 }
1638
1639 fn visit_bool<E: serde::de::Error>(self, value: bool) -> Result<Self::Value, E> {
1640 MeasuredJsonValue::scalar(StrictJsonValue::Bool(value))
1641 }
1642
1643 fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Self::Value, E> {
1644 if !safe_i64(value) {
1645 return Err(E::custom(
1646 "contact extension payload contains an RFC 8785 unsafe integer",
1647 ));
1648 }
1649 MeasuredJsonValue::scalar(StrictJsonValue::Number(value.into()))
1650 }
1651
1652 fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Self::Value, E> {
1653 if !safe_u64(value) {
1654 return Err(E::custom(
1655 "contact extension payload contains an RFC 8785 unsafe integer",
1656 ));
1657 }
1658 MeasuredJsonValue::scalar(StrictJsonValue::Number(value.into()))
1659 }
1660
1661 fn visit_f64<E: serde::de::Error>(self, value: f64) -> Result<Self::Value, E> {
1662 if !safe_f64(value) {
1663 return Err(E::custom(
1664 "contact extension payload contains a non-finite or RFC 8785 unsafe number",
1665 ));
1666 }
1667 serde_json::Number::from_f64(value)
1668 .map(StrictJsonValue::Number)
1669 .ok_or_else(|| E::custom("non-finite number"))
1670 .and_then(MeasuredJsonValue::scalar)
1671 }
1672
1673 fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
1674 if value.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1675 return Err(E::custom(
1676 "contact extension payload string exceeds V1 byte limit",
1677 ));
1678 }
1679 MeasuredJsonValue::scalar(StrictJsonValue::String(value.into()))
1680 }
1681
1682 fn visit_string<E: serde::de::Error>(self, value: String) -> Result<Self::Value, E> {
1683 if value.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1684 return Err(E::custom(
1685 "contact extension payload string exceeds V1 byte limit",
1686 ));
1687 }
1688 MeasuredJsonValue::scalar(StrictJsonValue::String(value))
1689 }
1690
1691 fn visit_seq<A>(self, mut values: A) -> Result<Self::Value, A::Error>
1692 where
1693 A: SeqAccess<'de>,
1694 {
1695 if self.depth > CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_DEPTH {
1696 return Err(A::Error::custom(
1697 "contact extension payload exceeds V1 nesting depth",
1698 ));
1699 }
1700 let mut output =
1701 Vec::with_capacity(values.size_hint().unwrap_or(0).min(MAX_OPAQUE_MEMBERS));
1702 let mut canonical_len = 2; while output.len() < MAX_OPAQUE_MEMBERS {
1704 let Some(child) = values.next_element_seed(MeasuredJsonValueSeed {
1705 depth: self.depth + 1,
1706 })?
1707 else {
1708 return Ok(MeasuredJsonValue {
1709 value: StrictJsonValue::Array(output),
1710 canonical_len,
1711 });
1712 };
1713 let candidate = canonical_len
1714 .saturating_add(child.canonical_len)
1715 .saturating_add(usize::from(!output.is_empty()));
1716 MeasuredJsonValue::bounded::<A::Error>(candidate)?;
1717 output.push(child.value);
1718 canonical_len = candidate;
1719 }
1720 let _ = values.next_element::<IgnoredAny>()?;
1721 Err(A::Error::custom(
1722 "contact extension payload nested array exceeds V1 bounded member limit",
1723 ))
1724 }
1725
1726 fn visit_map<A>(self, mut values: A) -> Result<Self::Value, A::Error>
1727 where
1728 A: MapAccess<'de>,
1729 {
1730 if self.depth > CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_DEPTH {
1731 return Err(A::Error::custom(
1732 "contact extension payload exceeds V1 nesting depth",
1733 ));
1734 }
1735 let mut output = BTreeMap::new();
1736 let mut canonical_len = 2; while output.len() < MAX_OPAQUE_MEMBERS {
1738 let Some(key) = values.next_key::<String>()? else {
1739 return Ok(MeasuredJsonValue {
1740 value: StrictJsonValue::Object(output),
1741 canonical_len,
1742 });
1743 };
1744 if key.len() > CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES {
1745 return Err(A::Error::custom(
1746 "contact extension payload object key exceeds V1 byte limit",
1747 ));
1748 }
1749 if output.contains_key(&key) {
1750 let _ = values.next_value::<IgnoredAny>()?;
1751 return Err(A::Error::custom(format!("duplicate object member {key:?}")));
1752 }
1753 let child = values.next_value_seed(MeasuredJsonValueSeed {
1754 depth: self.depth + 1,
1755 })?;
1756 let key_len = serde_jcs::to_vec(&key).map_err(A::Error::custom)?.len();
1757 let candidate = canonical_len
1758 .saturating_add(key_len)
1759 .saturating_add(1) .saturating_add(child.canonical_len)
1761 .saturating_add(usize::from(!output.is_empty()));
1762 MeasuredJsonValue::bounded::<A::Error>(candidate)?;
1763 output.insert(key, child.value);
1764 canonical_len = candidate;
1765 }
1766 let _ = values.next_key::<IgnoredAny>()?;
1767 Err(A::Error::custom(
1768 "contact extension payload nested object exceeds V1 bounded member limit",
1769 ))
1770 }
1771}