1use crate::heddle::api::common::{
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
75pub(crate) fn protobuf_string_field(
76 mut request: &[u8],
77 target_field: u32,
78) -> Result<Option<&str>, RequestMetadataError> {
79 let mut operation_id = None;
80 while !request.is_empty() {
81 let key = take_varint(&mut request)?;
82 let field = u32::try_from(key >> 3).map_err(|_| RequestMetadataError("field overflow"))?;
83 let wire = (key & 0x07) as u8;
84 if field == 0 || field > 0x1fff_ffff {
85 return Err(RequestMetadataError("invalid field number"));
86 }
87 if field == target_field && wire != 2 {
88 return Err(RequestMetadataError("operation id has wrong wire type"));
89 }
90 match wire {
91 0 => {
92 let _ = take_varint(&mut request)?;
93 }
94 1 => {
95 let _ = take_bytes(&mut request, 8)?;
96 }
97 2 => {
98 let length = usize::try_from(take_varint(&mut request)?)
99 .map_err(|_| RequestMetadataError("length overflow"))?;
100 let value = take_bytes(&mut request, length)?;
101 if field == target_field {
102 if operation_id.is_some() {
103 return Err(RequestMetadataError("duplicate operation id field"));
104 }
105 operation_id = Some(
106 std::str::from_utf8(value)
107 .map_err(|_| RequestMetadataError("operation id is not UTF-8"))?,
108 );
109 }
110 }
111 5 => {
112 let _ = take_bytes(&mut request, 4)?;
113 }
114 _ => return Err(RequestMetadataError("unsupported wire type")),
115 }
116 }
117 Ok(operation_id)
118}
119
120fn take_varint(input: &mut &[u8]) -> Result<u64, RequestMetadataError> {
121 let mut value = 0_u64;
122 for shift in (0..70).step_by(7) {
123 let (&byte, rest) = input
124 .split_first()
125 .ok_or(RequestMetadataError("truncated varint"))?;
126 *input = rest;
127 if shift == 63 && byte > 1 {
128 return Err(RequestMetadataError("varint overflow"));
129 }
130 value |= u64::from(byte & 0x7f) << shift;
131 if byte & 0x80 == 0 {
132 return Ok(value);
133 }
134 }
135 Err(RequestMetadataError("varint overflow"))
136}
137
138fn take_bytes<'a>(input: &mut &'a [u8], length: usize) -> Result<&'a [u8], RequestMetadataError> {
139 if input.len() < length {
140 return Err(RequestMetadataError("truncated field"));
141 }
142 let (value, rest) = input.split_at(length);
143 *input = rest;
144 Ok(value)
145}
146
147#[derive(Debug)]
150pub struct RoutedCall<'a> {
151 pub method: &'static MethodDescriptor,
153 pub context: &'a CallContext,
155}
156
157impl<'a> RoutedCall<'a> {
158 pub fn new(path: &str, context: &'a CallContext) -> Option<Self> {
160 method_descriptor(path).map(|method| Self { method, context })
161 }
162}