Skip to main content

animsmith_core/
contact_fragment.rs

1//! Strict, format-neutral contact-fragment V1 values.
2//!
3//! This module owns the interchange reader and canonical JSON seam. It does
4//! not infer contacts, load assets, perform transforms, or publish files.
5
6use 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
17/// Immutable schema identity for contact-fragment V1.
18pub const CONTACT_FRAGMENT_V1_ID: &str = "urn:animsmith:schema:contact-fragment:1";
19/// Immutable schema version for contact-fragment V1.
20pub const CONTACT_FRAGMENT_V1_SCHEMA_VERSION: u32 = 1;
21/// Maximum accepted UTF-8 JSON source bytes.
22pub const CONTACT_FRAGMENT_V1_MAX_SOURCE_BYTES: usize = 8 * 1024 * 1024;
23/// Maximum canonical RFC 8785 JSON bytes.
24pub const CONTACT_FRAGMENT_V1_MAX_CANONICAL_BYTES: usize = 8 * 1024 * 1024;
25/// Maximum core events per fragment.
26pub const CONTACT_FRAGMENT_V1_MAX_EVENTS: usize = 4_096;
27/// Maximum strict extension envelopes per fragment.
28pub const CONTACT_FRAGMENT_V1_MAX_EXTENSIONS: usize = 256;
29/// Maximum UTF-8 bytes in one ordinary authored string or object key.
30pub const CONTACT_FRAGMENT_V1_MAX_TEXT_BYTES: usize = 4_096;
31/// Maximum UTF-8 bytes in a V1 identifier.
32pub const CONTACT_FRAGMENT_V1_MAX_IDENTIFIER_BYTES: usize = 255;
33/// Maximum full-envelope object/array depth, including the root object.
34pub const CONTACT_FRAGMENT_V1_MAX_DEPTH: usize = 32;
35/// Maximum RFC 8785 bytes in one opaque extension payload.
36pub const CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES: usize = 256 * 1024;
37/// Maximum object/array depth within one extension payload.
38pub const CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_DEPTH: usize = 16;
39/// Largest exactly representable integer in the RFC 8785 / IEEE-754 JSON seam.
40///
41/// Contact-fragment V1 rejects integral JSON values outside this range before
42/// canonicalization. This is deliberately a contact-fragment contract, not a
43/// general replacement for the crate's identity authority.
44pub const CONTACT_FRAGMENT_V1_MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
45// A valid opaque payload with this many scalar members necessarily exceeds its
46// JCS-byte cap; this bounds generic nested collections before retention.
47const MAX_OPAQUE_MEMBERS: usize = CONTACT_FRAGMENT_V1_MAX_EXTENSION_PAYLOAD_BYTES;
48
49/// Reader or contract violation for contact-fragment V1.
50#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
51#[non_exhaustive]
52pub enum ContactFragmentError {
53    /// JSON source exceeded the immutable V1 input cap.
54    #[error("contact fragment source has {bytes} bytes, exceeding V1 limit {limit}")]
55    SourceTooLarge {
56        /// Observed source byte count.
57        bytes: usize,
58        /// Frozen V1 source byte limit.
59        limit: usize,
60    },
61    /// The source was not valid UTF-8 JSON or used duplicate object members.
62    #[error("invalid contact fragment JSON: {message}")]
63    InvalidJson {
64        /// Decoder detail, not a stable machine token.
65        message: String,
66    },
67    /// A required V1 field was absent, malformed, or an object contained an unknown field.
68    #[error("invalid contact fragment {field}: {message}")]
69    InvalidField {
70        /// Stable V1 field path.
71        field: &'static str,
72        /// Decoder detail, not a stable machine token.
73        message: String,
74    },
75    /// A frozen V1 row or byte bound was exceeded.
76    #[error("contact fragment {field} has {found}, exceeding V1 limit {limit}")]
77    LimitExceeded {
78        /// Stable V1 bounded field.
79        field: &'static str,
80        /// Observed count or byte length.
81        found: usize,
82        /// Frozen V1 limit.
83        limit: usize,
84    },
85    /// RFC 8785 canonical output could not be represented within the V1 cap.
86    #[error("contact fragment canonical JSON exceeds V1 limit {limit}")]
87    CanonicalTooLarge {
88        /// Frozen V1 canonical byte limit.
89        limit: usize,
90    },
91}
92
93/// The closed V1 semantic role vocabulary.
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
95#[serde(rename_all = "snake_case")]
96pub enum ContactRoleV1 {
97    /// Left foot.
98    LeftFoot,
99    /// Right foot.
100    RightFoot,
101    /// Left hand.
102    LeftHand,
103    /// Right hand.
104    RightHand,
105    /// Left toe.
106    LeftToe,
107    /// Right toe.
108    RightToe,
109    /// Left knee.
110    LeftKnee,
111    /// Right knee.
112    RightKnee,
113    /// Left elbow.
114    LeftElbow,
115    /// Right elbow.
116    RightElbow,
117    /// Root.
118    Root,
119    /// Prop.
120    Prop,
121    /// Body.
122    Body,
123}
124
125/// The closed V1 event-phase vocabulary.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
127#[serde(rename_all = "snake_case")]
128pub enum ContactPhaseV1 {
129    /// Start-like phase.
130    Begin,
131    /// End-like phase.
132    End,
133    /// One instantaneous marker.
134    Marker,
135}
136
137/// Exact V1 producer identity.
138#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
139pub struct ContactProducerV1 {
140    tool: String,
141    version: String,
142}
143
144impl ContactProducerV1 {
145    /// Construct a bounded producer identity.
146    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    /// Producer tool identifier.
160    pub fn tool(&self) -> &str {
161        &self.tool
162    }
163
164    /// Producer version identifier.
165    pub fn version(&self) -> &str {
166        &self.version
167    }
168}
169
170/// Exact selected clip witness.
171#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
172#[serde(tag = "scope", rename_all = "snake_case")]
173pub enum ContactClipReferenceV1 {
174    /// A uniquely named clip in one loaded document.
175    Document {
176        /// Exact embedded clip name.
177        clip_name: String,
178    },
179    /// A collection manifest witness and its exact source take.
180    Collection {
181        /// Logical clip identifier.
182        logical_id: String,
183        /// Collection source key.
184        source: String,
185        /// Exact source-local take index.
186        take_index: u32,
187        /// Exact source-local take name.
188        take_name: String,
189    },
190}
191
192impl ContactClipReferenceV1 {
193    /// Construct a document-scoped witness.
194    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    /// Construct a collection-scoped witness.
201    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/// Inclusive normalized time window.
235#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
236pub struct ContactEventWindowV1 {
237    start: f64,
238    end: f64,
239}
240
241impl ContactEventWindowV1 {
242    /// Construct a finite normalized window with `start <= end`.
243    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    /// Window start.
255    pub const fn start(self) -> f64 {
256        self.start
257    }
258    /// Window end.
259    pub const fn end(self) -> f64 {
260        self.end
261    }
262}
263
264/// The exactly-one point/window event shape.
265#[derive(Debug, Clone, Copy, PartialEq)]
266pub enum ContactEventKindV1 {
267    /// One normalized instant.
268    Point(f64),
269    /// One normalized interval.
270    Window(ContactEventWindowV1),
271}
272
273/// One stable event fact.
274#[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    /// Construct a point event.
285    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    /// Construct a window event.
301    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    /// Opaque stable event identifier.
346    pub fn event_id(&self) -> &str {
347        &self.event_id
348    }
349    /// Event role.
350    pub const fn role(&self) -> ContactRoleV1 {
351        self.role
352    }
353    /// Event phase.
354    pub const fn phase(&self) -> ContactPhaseV1 {
355        self.phase
356    }
357    /// Point or window shape.
358    pub const fn kind(&self) -> ContactEventKindV1 {
359        self.kind
360    }
361
362    /// Optional confidence in the closed interval `[0, 1]`.
363    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/// One strict extension envelope preserved as a generic JSON object payload.
389#[derive(Debug, Clone, PartialEq, Serialize)]
390pub struct ContactExtensionV1 {
391    schema: String,
392    schema_version: u32,
393    payload: Value,
394}
395
396impl ContactExtensionV1 {
397    /// Construct and bound an opaque extension payload.
398    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    /// Versioned extension schema identity.
435    pub fn schema(&self) -> &str {
436        &self.schema
437    }
438
439    /// Extension schema version.
440    pub const fn schema_version(&self) -> u32 {
441        self.schema_version
442    }
443
444    /// Opaque, strict-object extension payload.
445    pub fn payload(&self) -> &Value {
446        &self.payload
447    }
448}
449
450/// A validated, canonicalizable contact-fragment V1 envelope.
451#[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    /// Construct an envelope and impose the deterministic V1 event order.
467    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    /// Strictly decode one bounded UTF-8 JSON contact fragment.
520    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    /// RFC 8785 bytes, with the frozen V1 canonical-output bound.
560    pub fn canonical_json(&self) -> Result<Vec<u8>, ContactFragmentError> {
561        jcs(self, CONTACT_FRAGMENT_V1_MAX_CANONICAL_BYTES)
562    }
563    /// Identity of the exact canonical fragment bytes.
564    pub fn canonical_identity(&self) -> Result<InputIdentity, ContactFragmentError> {
565        Ok(InputIdentity::from_bytes(&self.canonical_json()?))
566    }
567
568    /// Exact tool and version that produced this fragment.
569    pub fn producer(&self) -> &ContactProducerV1 {
570        &self.producer
571    }
572
573    /// Events in frozen V1 canonical event order.
574    pub fn events(&self) -> &[ContactEventV1] {
575        &self.events
576    }
577
578    /// Exact source-artifact identity binding.
579    pub fn artifact(&self) -> &InputIdentity {
580        &self.artifact
581    }
582
583    /// Complete dependency-closure identity binding.
584    pub fn dependency_closure_identity(&self) -> &DependencyClosureIdentityV1 {
585        &self.dependency_closure_identity
586    }
587
588    /// Selected clip witness.
589    pub fn clip(&self) -> &ContactClipReferenceV1 {
590        &self.clip
591    }
592
593    /// Positive clip duration in seconds.
594    pub const fn duration_s(&self) -> f64 {
595        self.duration_s
596    }
597
598    /// Strict extension envelopes in declared array order.
599    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
1568/// A payload value plus its exact RFC 8785 byte count. This is deliberately
1569/// local to contact-fragment decoding: it keeps the payload budget enforced
1570/// while the parser is still deciding whether to retain a child.
1571struct 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; // []
1703        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; // {}
1737        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) // colon
1760                .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}