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;
21pub mod writer_authority;
22
23pub const NATIVE_HOST_WITNESS_V1_FIXTURE_JSON: &str =
25 include_str!("../tests/fixtures/native-host-witness-v1.json");
26
27pub const IMPORT_AUTHORITY_HOST_WITNESS_V1_FIXTURE_JSON: &str =
29 include_str!("../tests/fixtures/import-authority-host-witness-v1.json");
30
31pub use failure::{
32 ACCOUNT_BILLING_LOCK_POLICY_ID, account_billing_lock_error_detail,
33 account_billing_lock_from_error_detail, decode_account_billing_lock_error_detail,
34 encode_account_billing_lock_error_detail,
35};
36pub use transport::{
37 ALL_METHODS, HOSTED_ALPN_V1, MethodDescriptor, MethodRoute, PROVIDER_ALPN_V1,
38 RequestMetadataError, RoutedCall, StreamingShape, human_verification_challenge,
39 human_verification_error_detail, method_descriptor,
40};
41
42include!(concat!(
43 env!("OUT_DIR"),
44 "/heddle_api_attachment_authorization.rs"
45));
46
47pub const HOSTED_CALL_V1_FIXTURE_JSON: &str = include_str!("../tests/fixtures/hosted-call-v1.json");
49pub const UNARY_SIGNING_V1_FIXTURE_JSON: &str =
51 include_str!("../tests/fixtures/unary-signing-v1.json");
52pub const GRANT_ENVELOPE_V2_FIXTURE_JSON: &str =
54 include_str!("../tests/fixtures/grant-envelope-v2.json");
55pub const TRANSPORT_BOOTSTRAP_V1_FIXTURE_JSON: &str =
57 include_str!("../tests/fixtures/transport-bootstrap-v1.json");
58
59pub const DEFAULT_PAGE_SIZE: u32 = 50;
61pub const MAX_PAGE_SIZE: u32 = 200;
63
64pub const fn normalize_page_size(requested: u32) -> u32 {
66 if requested == 0 {
67 DEFAULT_PAGE_SIZE
68 } else if requested > MAX_PAGE_SIZE {
69 MAX_PAGE_SIZE
70 } else {
71 requested
72 }
73}
74
75pub mod heddle {
77 pub mod api {
79 pub mod common {
81 include!(concat!(env!("OUT_DIR"), "/heddle.api.common.rs"));
82 }
83 pub mod v1alpha2 {
85 include!(concat!(env!("OUT_DIR"), "/heddle.api.v1alpha2.rs"));
86 }
87 }
88}
89
90impl heddle::api::common::ErrorReason {
91 pub fn retryable(&self) -> bool {
93 matches!(
94 self,
95 Self::RateLimited | Self::QuotaExceeded | Self::Transient
96 )
97 }
98}
99
100#[cfg(feature = "reflection")]
102pub const FILE_DESCRIPTOR_SET: &[u8] =
103 include_bytes!(concat!(env!("OUT_DIR"), "/heddle_api_descriptor.bin"));
104
105#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct InvalidIdentifierLength {
108 kind: &'static str,
109 expected: usize,
110 actual: usize,
111}
112
113impl std::fmt::Display for InvalidIdentifierLength {
114 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 write!(
116 formatter,
117 "{} requires exactly {} bytes, got {}",
118 self.kind, self.expected, self.actual
119 )
120 }
121}
122
123impl std::error::Error for InvalidIdentifierLength {}
124
125impl heddle::api::common::StateId {
126 pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
128 fixed_width("StateId", 32, value.as_ref()).map(|value| Self { value })
129 }
130}
131
132impl heddle::api::common::ChangeId {
133 pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
135 fixed_width("ChangeId", 16, value.as_ref()).map(|value| Self { value })
136 }
137}
138
139impl heddle::api::common::OperationId {
140 pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
142 fixed_width("OperationId", 16, value.as_ref()).map(|value| Self { value })
143 }
144}
145
146impl heddle::api::common::OperationBatchId {
147 pub fn from_bytes(value: impl AsRef<[u8]>) -> Result<Self, InvalidIdentifierLength> {
149 fixed_width("OperationBatchId", 16, value.as_ref()).map(|value| Self { value })
150 }
151}
152
153impl heddle::api::common::GitObjectId {
154 pub fn from_digest(
156 algorithm: heddle::api::common::GitObjectAlgorithm,
157 digest: impl AsRef<[u8]>,
158 ) -> Result<Self, InvalidIdentifierLength> {
159 let expected = match algorithm {
160 heddle::api::common::GitObjectAlgorithm::Sha1 => 20,
161 heddle::api::common::GitObjectAlgorithm::Sha256 => 32,
162 heddle::api::common::GitObjectAlgorithm::Unspecified => 0,
163 };
164 fixed_width("GitObjectId", expected, digest.as_ref()).map(|digest| Self {
165 algorithm: algorithm as i32,
166 digest,
167 })
168 }
169}
170
171fn fixed_width(
172 kind: &'static str,
173 expected: usize,
174 value: &[u8],
175) -> Result<Vec<u8>, InvalidIdentifierLength> {
176 if value.len() != expected {
177 return Err(InvalidIdentifierLength {
178 kind,
179 expected,
180 actual: value.len(),
181 });
182 }
183 Ok(value.to_vec())
184}
185
186#[cfg(test)]
187mod tests {
188 use super::heddle::api::common::{
189 ChangeId, ErrorReason, GitObjectAlgorithm, GitObjectId, OperationBatchId, OperationId,
190 StateId,
191 };
192
193 #[test]
194 fn fixed_width_identifiers_reject_ambiguous_bytes() {
195 assert!(StateId::from_bytes([0; 32]).is_ok());
196 assert!(StateId::from_bytes([0; 31]).is_err());
197 assert!(ChangeId::from_bytes([0; 16]).is_ok());
198 assert!(ChangeId::from_bytes([0; 17]).is_err());
199 assert!(OperationId::from_bytes([0; 16]).is_ok());
200 assert!(OperationId::from_bytes([0; 15]).is_err());
201 assert!(OperationBatchId::from_bytes([0; 16]).is_ok());
202 assert!(OperationBatchId::from_bytes([0; 17]).is_err());
203 assert!(GitObjectId::from_digest(GitObjectAlgorithm::Sha1, [0; 20]).is_ok());
204 assert!(GitObjectId::from_digest(GitObjectAlgorithm::Sha256, [0; 20]).is_err());
205 }
206
207 #[test]
208 fn error_reason_retryability_is_derived_from_the_taxonomy() {
209 assert!(ErrorReason::RateLimited.retryable());
210 assert!(ErrorReason::QuotaExceeded.retryable());
211 assert!(ErrorReason::Transient.retryable());
212 assert!(!ErrorReason::CursorInvalid.retryable());
213 assert!(!ErrorReason::Internal.retryable());
214 }
215}