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