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