1use crate::heddle::api::v1alpha1::{
4 AuthorizationAccess, CallContext, DeploymentTarget, ErrorDetail, ErrorReason,
5 HumanVerificationChallenge, RetryBehavior, RpcEffect, ServiceMaturity, SigningTier,
6 error_detail,
7};
8
9pub const HOSTED_ALPN_V1: &[u8] = b"heddle-api/1";
11
12pub const PROVIDER_ALPN_V1: &[u8] = b"heddle-provider/1";
14
15pub fn human_verification_error_detail(challenge: HumanVerificationChallenge) -> ErrorDetail {
18 ErrorDetail {
19 reason: ErrorReason::PolicyDenied as i32,
20 resource: String::new(),
21 field: String::new(),
22 context: Some(error_detail::Context::HumanVerification(challenge)),
23 }
24}
25
26pub fn human_verification_challenge(detail: &ErrorDetail) -> Option<HumanVerificationChallenge> {
28 match &detail.context {
29 Some(error_detail::Context::HumanVerification(challenge)) => Some(challenge.clone()),
30 _ => None,
31 }
32}
33
34#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
36pub enum StreamingShape {
37 Unary,
39 ClientStreaming,
41 ServerStreaming,
43 Bidirectional,
45}
46
47include!(concat!(env!("OUT_DIR"), "/heddle_api_methods.rs"));
48
49impl MethodDescriptor {
50 pub const fn allows_zero_rtt(&self) -> bool {
52 matches!(self.effect, RpcEffect::ReadOnly)
53 && matches!(self.retry_behavior, RetryBehavior::Safe)
54 }
55
56 pub fn client_operation_id<'a>(
60 &self,
61 request: &'a [u8],
62 ) -> Result<Option<&'a str>, RequestMetadataError> {
63 let Some(field_number) = self.client_operation_id_field_number else {
64 return Ok(None);
65 };
66 protobuf_string_field(request, field_number)
67 }
68}
69
70#[derive(Debug, thiserror::Error)]
72#[error("invalid request metadata protobuf: {0}")]
73pub struct RequestMetadataError(&'static str);
74
75fn protobuf_string_field(
76 mut request: &[u8],
77 target_field: u32,
78) -> Result<Option<&str>, RequestMetadataError> {
79 while !request.is_empty() {
80 let key = take_varint(&mut request)?;
81 let field = u32::try_from(key >> 3).map_err(|_| RequestMetadataError("field overflow"))?;
82 let wire = (key & 0x07) as u8;
83 if field == 0 {
84 return Err(RequestMetadataError("field zero"));
85 }
86 match wire {
87 0 => {
88 let _ = take_varint(&mut request)?;
89 }
90 1 => {
91 let _ = take_bytes(&mut request, 8)?;
92 }
93 2 => {
94 let length = usize::try_from(take_varint(&mut request)?)
95 .map_err(|_| RequestMetadataError("length overflow"))?;
96 let value = take_bytes(&mut request, length)?;
97 if field == target_field {
98 return std::str::from_utf8(value)
99 .map(Some)
100 .map_err(|_| RequestMetadataError("operation id is not UTF-8"));
101 }
102 }
103 5 => {
104 let _ = take_bytes(&mut request, 4)?;
105 }
106 _ => return Err(RequestMetadataError("unsupported wire type")),
107 }
108 }
109 Ok(None)
110}
111
112fn take_varint(input: &mut &[u8]) -> Result<u64, RequestMetadataError> {
113 let mut value = 0_u64;
114 for shift in (0..70).step_by(7) {
115 let (&byte, rest) = input
116 .split_first()
117 .ok_or(RequestMetadataError("truncated varint"))?;
118 *input = rest;
119 if shift == 63 && byte > 1 {
120 return Err(RequestMetadataError("varint overflow"));
121 }
122 value |= u64::from(byte & 0x7f) << shift;
123 if byte & 0x80 == 0 {
124 return Ok(value);
125 }
126 }
127 Err(RequestMetadataError("varint overflow"))
128}
129
130fn take_bytes<'a>(input: &mut &'a [u8], length: usize) -> Result<&'a [u8], RequestMetadataError> {
131 if input.len() < length {
132 return Err(RequestMetadataError("truncated field"));
133 }
134 let (value, rest) = input.split_at(length);
135 *input = rest;
136 Ok(value)
137}
138
139#[derive(Debug)]
142pub struct RoutedCall<'a> {
143 pub method: &'static MethodDescriptor,
145 pub context: &'a CallContext,
147}
148
149impl<'a> RoutedCall<'a> {
150 pub fn new(path: &str, context: &'a CallContext) -> Option<Self> {
152 method_descriptor(path).map(|method| Self { method, context })
153 }
154}