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