qubit-value 0.13.1

Type-safe containers for single, multi-valued, and named runtime values
Documentation
// =============================================================================
//    Copyright (c) 2025 - 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================

//! Stable duration payload used by scalar and collection adapters.

use std::time::Duration;

use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;

use super::duration_payload_visitor::DurationPayloadVisitor;

/// Encodes a [`Duration`] as whole seconds and a fractional nanosecond count.
///
/// Payloads created from a [`Duration`] have a nanosecond component less than
/// one billion. Deserialized payloads are checked for this range when converted
/// back into a [`Duration`].
#[derive(Serialize)]
pub(in crate::wire) struct DurationPayload {
    /// Number of complete seconds in the represented duration.
    pub(in crate::wire) secs: u64,
    /// Fractional nanoseconds within the final second; conversion to
    /// [`Duration`] rejects values above 999,999,999.
    pub(in crate::wire) nanos: u32,
}

impl<'de> Deserialize<'de> for DurationPayload {
    /// Selects the payload representation from the deserializer's format hint.
    ///
    /// The named struct is requested in all formats. Human-readable formats
    /// must provide named fields; other formats may use the ordered sequence
    /// representation. The visitor enforces the field and element constraints.
    ///
    /// # Type Parameters
    ///
    /// - `D`: The Serde deserializer that supplies the payload representation.
    ///
    /// # Parameters
    ///
    /// - `deserializer`: The input decoder, consumed to read the payload
    ///   fields.
    ///
    /// # Returns
    ///
    /// The decoded seconds and nanoseconds, without validating the nanosecond
    /// range.
    ///
    /// # Errors
    ///
    /// Returns the deserializer's error for an unsupported shape, malformed
    /// field value, duplicate or unknown map field, missing field, or a
    /// sequence whose length is not two. The nanosecond range is checked later
    /// when converting this payload to [`Duration`].
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let allow_sequence = !deserializer.is_human_readable();
        deserializer.deserialize_struct(
            "DurationPayload",
            &["secs", "nanos"],
            DurationPayloadVisitor::new(allow_sequence),
        )
    }
}

impl From<&Duration> for DurationPayload {
    /// Splits a runtime duration into its seconds and fractional nanoseconds.
    ///
    /// # Parameters
    ///
    /// - `value`: The duration whose components are encoded.
    ///
    /// # Returns
    ///
    /// A payload with the seconds and subsecond nanoseconds from `value`.
    #[inline]
    fn from(value: &Duration) -> Self {
        Self {
            secs: value.as_secs(),
            nanos: value.subsec_nanos(),
        }
    }
}

impl TryFrom<DurationPayload> for Duration {
    /// Static validation message for an out-of-range nanosecond component.
    type Error = &'static str;

    /// Restores a duration after validating its nanosecond component.
    ///
    /// # Parameters
    ///
    /// - `value`: The serialized seconds and fractional nanoseconds.
    ///
    /// # Returns
    ///
    /// The duration represented by `value` when its nanosecond component is
    /// valid.
    ///
    /// # Errors
    ///
    /// Returns a static error message when `value.nanos` is at least one
    /// billion.
    #[inline]
    fn try_from(value: DurationPayload) -> Result<Self, Self::Error> {
        if value.nanos >= 1_000_000_000 {
            return Err("duration nanoseconds must be less than 1000000000");
        }
        Ok(Self::new(value.secs, value.nanos))
    }
}