1pub 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
21pub const NATIVE_HOST_WITNESS_V1_FIXTURE_JSON: &str =
23 include_str!("../tests/fixtures/native-host-witness-v1.json");
24
25pub 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
45pub const HOSTED_CALL_V1_FIXTURE_JSON: &str = include_str!("../tests/fixtures/hosted-call-v1.json");
47pub const UNARY_SIGNING_V1_FIXTURE_JSON: &str =
49 include_str!("../tests/fixtures/unary-signing-v1.json");
50pub const GRANT_ENVELOPE_V2_FIXTURE_JSON: &str =
52 include_str!("../tests/fixtures/grant-envelope-v2.json");
53pub const TRANSPORT_BOOTSTRAP_V1_FIXTURE_JSON: &str =
55 include_str!("../tests/fixtures/transport-bootstrap-v1.json");
56
57pub const DEFAULT_PAGE_SIZE: u32 = 50;
59pub const MAX_PAGE_SIZE: u32 = 200;
61
62pub 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
73pub mod heddle {
75 pub mod api {
77 pub mod common {
79 include!(concat!(env!("OUT_DIR"), "/heddle.api.common.rs"));
80 }
81 pub mod v1alpha2 {
83 include!(concat!(env!("OUT_DIR"), "/heddle.api.v1alpha2.rs"));
84 }
85 }
86}
87
88impl heddle::api::common::ErrorReason {
89 pub fn retryable(&self) -> bool {
91 matches!(
92 self,
93 Self::RateLimited | Self::QuotaExceeded | Self::Transient
94 )
95 }
96}
97
98#[cfg(feature = "reflection")]
100pub const FILE_DESCRIPTOR_SET: &[u8] =
101 include_bytes!(concat!(env!("OUT_DIR"), "/heddle_api_descriptor.bin"));
102
103#[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 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 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 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 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 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}