cratestack_axum/envelope_layer/
build.rs1use std::sync::Arc;
4
5use cratestack_core::CratestackError;
6use http::HeaderValue;
7
8use super::builder::{EnvelopeLayerBuilder, Transport};
9use super::layer::{Config, EnvelopeLayer};
10use super::media;
11use super::mode::{EnvelopePolicy, WithUnresolved};
12use super::resolver::BindingResolver;
13use super::resolver_rest::RestBindingResolver;
14use super::resolver_rpc::RpcBindingResolver;
15use crate::idempotency::mount_prefix;
16
17impl EnvelopeLayerBuilder {
18 pub fn build(self) -> Result<EnvelopeLayer, CratestackError> {
26 if self.audience.is_empty() {
27 return Err(invalid("the envelope layer's audience must not be empty"));
28 }
29 let policy = self
30 .policy
31 .ok_or_else(|| invalid("the envelope layer has no policy (decision D9: no default)"))?;
32 let policy: Box<dyn EnvelopePolicy> = match self.unresolved_mode {
33 Some(unresolved) => Box::new(WithUnresolved { policy, unresolved }),
34 None => policy,
35 };
36 let prefix = self
38 .mount_prefix
39 .as_deref()
40 .or(self.transport_prefix.as_deref())
41 .unwrap_or("");
42 let resolver: Box<dyn BindingResolver> = match self.transport {
43 Transport::None => {
44 return Err(invalid(
45 "the envelope layer needs a transport: rest(..), rpc(..) or binding_resolver(..)",
46 ));
47 }
48 Transport::Rest([]) => {
49 return Err(invalid(
50 "rest(..) was given an empty route table: pass the generated \
51 ROUTE_TRANSPORTS of a REST schema, or use rpc(..) for `transport rpc`",
52 ));
53 }
54 Transport::Rest(routes) => Box::new(RestBindingResolver::new(prefix, routes)),
55 Transport::Rpc => Box::new(RpcBindingResolver::new(prefix)),
56 Transport::Custom(resolver) => resolver,
57 };
58 if self.max_contract_trials == 0 {
59 return Err(invalid(
60 "the envelope layer's contract trials must be at least 1",
61 ));
62 }
63 if self.max_body_bytes == 0 {
64 return Err(invalid("the envelope layer's body limit must not be zero"));
65 }
66 if let Some(bad) = self.allow_unresolved.iter().find(|t| !t.starts_with('/')) {
67 return Err(invalid(&format!(
68 "allow_unresolved({bad:?}): a route template starts with '/'"
69 )));
70 }
71 super::builder_payload::validate(&self.payload_request, &self.payload_response)?;
72 let media_type = self.envelope.media_type();
73 if HeaderValue::from_str(media_type).is_err()
74 || !media::is_envelope_media_type(media_type, &*self.envelope)
75 {
76 return Err(invalid(
77 "the envelope's media type must be a valid header value naming \
78 application/cose or a type the envelope claims (is_envelope_content_type)",
79 ));
80 }
81 Ok(EnvelopeLayer {
82 config: Arc::new(Config {
83 envelope: self.envelope,
84 policy,
85 resolver,
86 principal: self.principal,
87 seal_policy: self.seal_policy,
88 audience: self.audience,
89 contracts: self.contracts,
90 max_contract_trials: self.max_contract_trials,
91 max_body_bytes: self.max_body_bytes,
92 mount_prefix: mount_prefix::normalize(prefix),
93 allow_unresolved: self.allow_unresolved,
94 payload_request: self.payload_request,
95 payload_response: self.payload_response,
96 }),
97 })
98 }
99}
100
101fn invalid(message: &str) -> CratestackError {
102 CratestackError::Validation(message.to_owned())
103}