Skip to main content

qubit_progress/
stage.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Optional operation stage metadata.
9
10use std::sync::Arc;
11
12#[cfg(feature = "serde")]
13use serde::Deserialize;
14#[cfg(feature = "serde")]
15use serde::Deserializer;
16#[cfg(feature = "serde")]
17use serde::Serialize;
18#[cfg(feature = "serde")]
19use serde::de::Error;
20
21#[cfg(feature = "serde")]
22use crate::validation::validate_stage;
23
24/// Human-readable sub-stage attached to subsequently emitted events.
25#[cfg_attr(feature = "serde", derive(Serialize))]
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub struct Stage {
28    /// Machine-readable stage identifier.
29    pub(crate) id: Arc<str>,
30    /// Human-readable stage name.
31    pub(crate) name: Arc<str>,
32    /// One-based stage position when present.
33    pub(crate) position: Option<u64>,
34    /// Number of stages when present.
35    pub(crate) total: Option<u64>,
36}
37
38/// Serializable wire representation used to validate standalone stages.
39#[cfg(feature = "serde")]
40#[derive(Deserialize)]
41struct StageWire {
42    /// Machine-readable stage identifier.
43    id: Arc<str>,
44    /// Human-readable stage name.
45    name: Arc<str>,
46    /// One-based stage position when present.
47    position: Option<u64>,
48    /// Number of stages when present.
49    total: Option<u64>,
50}
51
52#[cfg(feature = "serde")]
53impl<'de> Deserialize<'de> for Stage {
54    /// Deserializes and validates one standalone stage.
55    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56    where
57        D: Deserializer<'de>,
58    {
59        let wire = StageWire::deserialize(deserializer)?;
60        let stage = Self {
61            id: wire.id,
62            name: wire.name,
63            position: wire.position,
64            total: wire.total,
65        };
66        validate_stage(&stage).map_err(Error::custom)?;
67        Ok(stage)
68    }
69}
70
71impl Stage {
72    /// Creates stage metadata without sequence position information.
73    #[must_use]
74    pub fn new(id: &str, name: &str) -> Self {
75        Self {
76            id: Arc::from(id),
77            name: Arc::from(name),
78            position: None,
79            total: None,
80        }
81    }
82    /// Sets one-based stage position and total number of stages.
83    #[must_use]
84    pub const fn position(mut self, position: u64, total: u64) -> Self {
85        self.position = Some(position);
86        self.total = Some(total);
87        self
88    }
89    /// Returns the stage's stable ID.
90    #[must_use]
91    pub fn id(&self) -> &str {
92        &self.id
93    }
94    /// Returns the stage's display name.
95    #[must_use]
96    pub fn name(&self) -> &str {
97        &self.name
98    }
99    /// Returns the one-based position, if present.
100    #[must_use]
101    pub const fn position_value(&self) -> Option<u64> {
102        self.position
103    }
104    /// Returns the stage count, if present.
105    #[must_use]
106    pub const fn total(&self) -> Option<u64> {
107        self.total
108    }
109}