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