1pub 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
22pub const NATIVE_HOST_WITNESS_V1_FIXTURE_JSON: &str =
24 include_str!("../tests/fixtures/native-host-witness-v1.json");
25
26pub 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
46pub const HOSTED_CALL_V1_FIXTURE_JSON: &str = include_str!("../tests/fixtures/hosted-call-v1.json");
48pub const UNARY_SIGNING_V1_FIXTURE_JSON: &str =
50 include_str!("../tests/fixtures/unary-signing-v1.json");
51pub const GRANT_ENVELOPE_V2_FIXTURE_JSON: &str =
53 include_str!("../tests/fixtures/grant-envelope-v2.json");
54pub const TRANSPORT_BOOTSTRAP_V1_FIXTURE_JSON: &str =
56 include_str!("../tests/fixtures/transport-bootstrap-v1.json");
57
58pub const DEFAULT_PAGE_SIZE: u32 = 50;
60pub const MAX_PAGE_SIZE: u32 = 200;
62
63pub 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
74pub mod heddle {
76 pub mod api {
78 pub mod common {
80 include!(concat!(env!("OUT_DIR"), "/heddle.api.common.rs"));
81 }
82 pub mod v1alpha2 {
84 include!(concat!(env!("OUT_DIR"), "/heddle.api.v1alpha2.rs"));
85 }
86 }
87}
88
89impl heddle::api::common::ErrorReason {
90 pub fn retryable(&self) -> bool {
92 matches!(
93 self,
94 Self::RateLimited | Self::QuotaExceeded | Self::Transient
95 )
96 }
97}
98
99#[cfg(feature = "reflection")]
101pub const FILE_DESCRIPTOR_SET: &[u8] =
102 include_bytes!(concat!(env!("OUT_DIR"), "/heddle_api_descriptor.bin"));
103
104#[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 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 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 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 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 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}