Skip to main content

heddle_api/
lib.rs

1//! Generated transport-neutral Rust types for the Heddle API.
2
3pub mod descriptor_trust;
4mod failure;
5pub mod framing;
6pub mod mint_root_association;
7pub mod passkey_mint_grant;
8pub mod password_owner;
9pub mod provider_v2;
10pub mod request_proof;
11pub mod signing;
12pub mod timeline_upload;
13mod transport;
14pub mod treadle;
15pub mod v2;
16
17pub use failure::{
18    ACCOUNT_BILLING_LOCK_POLICY_ID, account_billing_lock_error_detail,
19    account_billing_lock_from_error_detail, decode_account_billing_lock_error_detail,
20    encode_account_billing_lock_error_detail,
21};
22pub use transport::{
23    ALL_METHODS, HOSTED_ALPN_V1, MethodDescriptor, MethodRoute, PROVIDER_ALPN_V1,
24    RequestMetadataError, RoutedCall, StreamingShape, human_verification_challenge,
25    human_verification_error_detail, method_descriptor,
26};
27
28include!(concat!(
29    env!("OUT_DIR"),
30    "/heddle_api_attachment_authorization.rs"
31));
32
33/// Cross-product hosted-call framing and typed-failure fixture.
34pub const HOSTED_CALL_V1_FIXTURE_JSON: &str = include_str!("../tests/fixtures/hosted-call-v1.json");
35/// Cross-product canonical unary-signing fixture.
36pub const UNARY_SIGNING_V1_FIXTURE_JSON: &str =
37    include_str!("../tests/fixtures/unary-signing-v1.json");
38/// Cross-product canonical GrantEnvelope v2 payload and rejection fixtures.
39pub const GRANT_ENVELOPE_V2_FIXTURE_JSON: &str =
40    include_str!("../tests/fixtures/grant-envelope-v2.json");
41/// Cross-product endpoint-descriptor and relay-admission signing fixture.
42pub const TRANSPORT_BOOTSTRAP_V1_FIXTURE_JSON: &str =
43    include_str!("../tests/fixtures/transport-bootstrap-v1.json");
44
45/// Page size used when callers omit or pass zero for a requested size.
46pub const DEFAULT_PAGE_SIZE: u32 = 50;
47/// Largest page the public API permits.
48pub const MAX_PAGE_SIZE: u32 = 200;
49
50/// Applies the contract-owned default and upper bound to a requested page.
51pub const fn normalize_page_size(requested: u32) -> u32 {
52    if requested == 0 {
53        DEFAULT_PAGE_SIZE
54    } else if requested > MAX_PAGE_SIZE {
55        MAX_PAGE_SIZE
56    } else {
57        requested
58    }
59}
60
61/// Heddle API protobuf packages.
62pub mod heddle {
63    /// Neutral public API contract.
64    pub mod api {
65        /// Shared foundational types used by every versioned API package.
66        pub mod common {
67            include!(concat!(env!("OUT_DIR"), "/heddle.api.common.rs"));
68        }
69        /// Frozen Thread-oriented contract; endpoint support is negotiated.
70        pub mod v1alpha2 {
71            include!(concat!(env!("OUT_DIR"), "/heddle.api.v1alpha2.rs"));
72        }
73    }
74}
75
76impl heddle::api::common::ErrorReason {
77    /// Returns whether callers may retry without first correcting the request.
78    pub fn retryable(&self) -> bool {
79        matches!(
80            self,
81            Self::RateLimited | Self::QuotaExceeded | Self::Transient
82        )
83    }
84}
85
86/// Compiled protobuf descriptor set for reflection and contract inspection.
87#[cfg(feature = "reflection")]
88pub const FILE_DESCRIPTOR_SET: &[u8] =
89    include_bytes!(concat!(env!("OUT_DIR"), "/heddle_api_descriptor.bin"));
90
91/// Errors returned while constructing fixed-width API identifiers.
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct InvalidIdentifierLength {
94    kind: &'static str,
95    expected: usize,
96    actual: usize,
97}
98
99impl std::fmt::Display for InvalidIdentifierLength {
100    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        write!(
102            formatter,
103            "{} requires exactly {} bytes, got {}",
104            self.kind, self.expected, self.actual
105        )
106    }
107}
108
109impl std::error::Error for InvalidIdentifierLength {}
110
111impl heddle::api::common::StateId {
112    /// Constructs a physical state identifier from exactly 32 bytes.
113    pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
114        fixed_width("StateId", 32, value.as_ref()).map(|value| Self { value })
115    }
116}
117
118impl heddle::api::common::ChangeId {
119    /// Constructs a rewrite-stable change identifier from exactly 16 bytes.
120    pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
121        fixed_width("ChangeId", 16, value.as_ref()).map(|value| Self { value })
122    }
123}
124
125impl heddle::api::common::OperationId {
126    /// Constructs a durable operation identifier from exactly 16 bytes.
127    pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
128        fixed_width("OperationId", 16, value.as_ref()).map(|value| Self { value })
129    }
130}
131
132impl heddle::api::common::OperationBatchId {
133    /// Constructs a durable operation-batch identifier from exactly 16 bytes.
134    pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
135        fixed_width("OperationBatchId", 16, value.as_ref()).map(|value| Self { value })
136    }
137}
138
139impl heddle::api::common::GitObjectId {
140    /// Constructs and validates a Git object identifier for its hash algorithm.
141    pub fn from_digest(
142        algorithm: heddle::api::common::GitObjectAlgorithm,
143        digest: impl AsRef<[u8]>,
144    ) -> Result<Self, InvalidIdentifierLength> {
145        let expected = match algorithm {
146            heddle::api::common::GitObjectAlgorithm::Sha1 => 20,
147            heddle::api::common::GitObjectAlgorithm::Sha256 => 32,
148            heddle::api::common::GitObjectAlgorithm::Unspecified => 0,
149        };
150        fixed_width("GitObjectId", expected, digest.as_ref()).map(|digest| Self {
151            algorithm: algorithm as i32,
152            digest,
153        })
154    }
155}
156
157fn fixed_width(
158    kind: &'static str,
159    expected: usize,
160    value: &[u8],
161) -> Result<Vec<u8>, InvalidIdentifierLength> {
162    if value.len() != expected {
163        return Err(InvalidIdentifierLength {
164            kind,
165            expected,
166            actual: value.len(),
167        });
168    }
169    Ok(value.to_vec())
170}
171
172#[cfg(test)]
173mod tests {
174    use super::heddle::api::common::{
175        ChangeId, ErrorReason, GitObjectAlgorithm, GitObjectId, OperationBatchId, OperationId,
176        StateId,
177    };
178
179    #[test]
180    fn fixed_width_identifiers_reject_ambiguous_bytes() {
181        assert!(StateId::from_bytes([0; 32]).is_ok());
182        assert!(StateId::from_bytes([0; 31]).is_err());
183        assert!(ChangeId::from_bytes([0; 16]).is_ok());
184        assert!(ChangeId::from_bytes([0; 17]).is_err());
185        assert!(OperationId::from_bytes([0; 16]).is_ok());
186        assert!(OperationId::from_bytes([0; 15]).is_err());
187        assert!(OperationBatchId::from_bytes([0; 16]).is_ok());
188        assert!(OperationBatchId::from_bytes([0; 17]).is_err());
189        assert!(GitObjectId::from_digest(GitObjectAlgorithm::Sha1, [0; 20]).is_ok());
190        assert!(GitObjectId::from_digest(GitObjectAlgorithm::Sha256, [0; 20]).is_err());
191    }
192
193    #[test]
194    fn error_reason_retryability_is_derived_from_the_taxonomy() {
195        assert!(ErrorReason::RateLimited.retryable());
196        assert!(ErrorReason::QuotaExceeded.retryable());
197        assert!(ErrorReason::Transient.retryable());
198        assert!(!ErrorReason::CursorInvalid.retryable());
199        assert!(!ErrorReason::Internal.retryable());
200    }
201}