use super::wire::ValueNodeIndex;
use super::{GuestPermissionCardHandle, GuestQuotaTokenHandle, GuestSecretHandle, wire};
use crate::schema::SchemaValueStream;
use crate::schema::{Quantity, QuantityUnit, QuantityValue};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::rc::Rc;
#[derive(Clone, Default)]
pub struct WireSchemaBuilder {
type_nodes: Vec<wire::SchemaTypeNode>,
defs: Vec<wire::SchemaTypeDef>,
named: HashMap<String, wire::DefIndex>,
claimants: HashMap<String, &'static str>,
candidate_requests: HashMap<wire::DefIndex, &'static str>,
active_candidates: HashMap<wire::DefIndex, &'static str>,
validated_claimants: HashSet<(wire::DefIndex, &'static str)>,
}
pub enum WireSchemaReservation {
Fresh,
Recursive,
Occupied,
}
impl WireSchemaBuilder {
pub fn push(&mut self, body: wire::SchemaTypeBody) -> wire::TypeNodeIndex {
self.push_with_metadata(body, empty_metadata())
}
pub fn push_with_metadata(
&mut self,
body: wire::SchemaTypeBody,
metadata: wire::MetadataEnvelope,
) -> wire::TypeNodeIndex {
let index = self.type_nodes.len() as wire::TypeNodeIndex;
self.type_nodes
.push(wire::SchemaTypeNode { body, metadata });
index
}
pub fn reserve(&mut self, id: String, name: Option<String>) -> (wire::DefIndex, bool) {
if let Some(index) = self.named.get(&id) {
return (*index, false);
}
let index = self.defs.len() as wire::DefIndex;
self.named.insert(id.clone(), index);
self.defs.push(wire::SchemaTypeDef { id, name, body: -1 });
(index, true)
}
pub fn reserve_nominal(
&mut self,
id: String,
name: Option<String>,
claimant: &'static str,
) -> (wire::DefIndex, WireSchemaReservation) {
if let Some(&definition) = self.named.get(&id) {
let owner = self
.claimants
.get(&id)
.copied()
.unwrap_or("<manual WireSchema implementation>");
let pending = self.defs[definition as usize].body < 0;
if pending && owner != claimant {
panic!("wire schema id `{id}` is claimed by both `{owner}` and `{claimant}`");
}
if let Some(active_claimant) = self.active_candidates.get(&definition)
&& *active_claimant != claimant
{
panic!(
"wire schema id `{id}` is claimed by both `{active_claimant}` and `{claimant}`"
);
}
let state = if pending
|| self.active_candidates.get(&definition) == Some(&claimant)
|| self.validated_claimants.contains(&(definition, claimant))
{
WireSchemaReservation::Recursive
} else {
self.candidate_requests.insert(definition, claimant);
WireSchemaReservation::Occupied
};
return (definition, state);
}
let (definition, fresh) = self.reserve(id.clone(), name);
debug_assert!(fresh);
self.claimants.insert(id, claimant);
(definition, WireSchemaReservation::Fresh)
}
pub fn begin_candidate(&mut self, definition: wire::DefIndex) -> Self {
let claimant = self
.candidate_requests
.remove(&definition)
.expect("occupied nominal reservation must precede candidate validation");
let checkpoint = self.clone();
self.active_candidates.insert(definition, claimant);
checkpoint
}
pub fn verify_candidate(
&mut self,
checkpoint: Self,
definition: wire::DefIndex,
candidate_body: wire::TypeNodeIndex,
) {
let existing_body = self.defs[definition as usize].body;
let mut arena = self.clone();
let placeholder = arena.push(wire::SchemaTypeBody::TupleType(Vec::new()));
for definition in &mut arena.defs {
if definition.body < 0 {
definition.body = placeholder;
}
}
let mut existing_wire = arena.clone().finish(existing_body);
existing_wire.root = existing_body;
let mut candidate_wire = arena.finish(candidate_body);
candidate_wire.root = candidate_body;
let existing = super::decode::decode_graph(&existing_wire)
.expect("generated wire schema must form a valid flat arena");
let candidate = super::decode::decode_graph(&candidate_wire)
.expect("generated wire schema candidate must form a valid flat arena");
if !crate::schema::validation::is_equivalent_cross_graph(
&existing,
&existing.root,
&candidate,
&candidate.root,
) {
let id = &self.defs[definition as usize].id;
let claimant = self
.claimants
.get(id)
.copied()
.unwrap_or("<manual WireSchema implementation>");
panic!("wire schema id `{id}` has a divergent implementation in `{claimant}`");
}
let claimant = self
.active_candidates
.get(&definition)
.copied()
.expect("candidate must remain active until validation completes");
*self = checkpoint;
self.validated_claimants.insert((definition, claimant));
}
pub fn commit(&mut self, definition: wire::DefIndex, body: wire::TypeNodeIndex) {
self.defs[definition as usize].body = body;
}
pub fn reference(&mut self, definition: wire::DefIndex) -> wire::TypeNodeIndex {
self.push(wire::SchemaTypeBody::RefType(definition))
}
pub fn node(&self, index: wire::TypeNodeIndex) -> Option<&wire::SchemaTypeNode> {
self.type_nodes.get(usize::try_from(index).ok()?)
}
pub fn resolve(&self, mut index: wire::TypeNodeIndex) -> Option<&wire::SchemaTypeNode> {
for _ in 0..=self.defs.len() {
let node = self.node(index)?;
match node.body {
wire::SchemaTypeBody::RefType(definition) => {
index = self.defs.get(usize::try_from(definition).ok()?)?.body;
}
_ => return Some(node),
}
}
None
}
pub fn finish(self, root: wire::TypeNodeIndex) -> wire::SchemaGraph {
wire::SchemaGraph {
type_nodes: self.type_nodes,
defs: self.defs,
root,
}
}
}
pub fn empty_metadata() -> wire::MetadataEnvelope {
wire::MetadataEnvelope {
doc: None,
aliases: Vec::new(),
examples: Vec::new(),
deprecated: None,
role: None,
}
}
pub trait WireSchema {
const IS_UNIT: bool = false;
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex;
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
let _ = seen;
false
}
fn wire_type_id() -> String {
std::any::type_name::<Self>().replace("::", ".")
}
}
pub fn schema<T: WireSchema + ?Sized>() -> wire::SchemaGraph {
let mut builder = WireSchemaBuilder::default();
let root = T::append_schema(&mut builder);
builder.finish(root)
}
macro_rules! wire_schema_scalar {
($($ty:ty => $body:expr),* $(,)?) => {$(
impl WireSchema for $ty {
fn wire_type_id() -> String {
<Self as crate::schema::IntoSchema>::type_id().0
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push($body)
}
}
)*};
}
wire_schema_scalar! {
bool => wire::SchemaTypeBody::BoolType,
i8 => wire::SchemaTypeBody::S8Type(None), i16 => wire::SchemaTypeBody::S16Type(None),
i32 => wire::SchemaTypeBody::S32Type(None), i64 => wire::SchemaTypeBody::S64Type(None),
u8 => wire::SchemaTypeBody::U8Type(None), u16 => wire::SchemaTypeBody::U16Type(None),
u32 => wire::SchemaTypeBody::U32Type(None), u64 => wire::SchemaTypeBody::U64Type(None),
f32 => wire::SchemaTypeBody::F32Type(None), f64 => wire::SchemaTypeBody::F64Type(None),
char => wire::SchemaTypeBody::CharType, String => wire::SchemaTypeBody::StringType,
str => wire::SchemaTypeBody::StringType,
chrono::DateTime<chrono::Utc> => wire::SchemaTypeBody::DatetimeType,
std::time::Duration => wire::SchemaTypeBody::DurationType,
}
impl IntoWire for chrono::DateTime<chrono::Utc> {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(
writer.push(wire::SchemaValueNode::DatetimeValue(wire::Datetime {
seconds: self.timestamp(),
nanoseconds: self.timestamp_subsec_nanos(),
})),
)
}
}
impl FromWire for chrono::DateTime<chrono::Utc> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
let wire::SchemaValueNode::DatetimeValue(value) = reader.take(index)? else {
return Err(WireError::Shape("datetime"));
};
super::datetime_from_wire(&value).ok_or(WireError::Shape("valid datetime"))
}
}
impl IntoWire for std::time::Duration {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::DurationValue(
wire::DurationValuePayload {
nanoseconds: self.as_nanos().min(i64::MAX as u128) as i64,
},
)))
}
}
impl FromWire for std::time::Duration {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
let wire::SchemaValueNode::DurationValue(value) = reader.take(index)? else {
return Err(WireError::Shape("duration"));
};
let nanos = u64::try_from(value.nanoseconds)
.map_err(|_| WireError::Shape("nonnegative duration"))?;
Ok(Self::from_nanos(nanos))
}
}
#[cfg(feature = "url")]
impl WireSchema for url::Url {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::UrlType(wire::UrlRestrictions {
allowed_schemes: None,
allowed_hosts: None,
}))
}
}
#[cfg(feature = "url")]
impl IntoWire for url::Url {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::UrlValue(self.to_string())))
}
}
#[cfg(feature = "url")]
impl FromWire for url::Url {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
let wire::SchemaValueNode::UrlValue(value) = reader.take(index)? else {
return Err(WireError::Shape("url"));
};
Self::parse(&value).map_err(|_| WireError::Shape("valid url"))
}
}
impl WireSchema for uuid::Uuid {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::UuidType)
}
fn wire_type_id() -> String {
"uuid.Uuid".into()
}
}
impl IntoWire for uuid::Uuid {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let (high_bits, low_bits) = self.as_u64_pair();
Ok(writer.push(wire::SchemaValueNode::UuidValue(wire::Uuid {
high_bits,
low_bits,
})))
}
}
impl FromWire for uuid::Uuid {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
let wire::SchemaValueNode::UuidValue(value) = reader.take(index)? else {
return Err(WireError::Shape("uuid"));
};
Ok(Self::from_u64_pair(value.high_bits, value.low_bits))
}
}
impl<U: QuantityUnit> WireSchema for Quantity<U> {
fn wire_type_id() -> String {
<Self as crate::schema::IntoSchema>::type_id().0
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::QuantityType(wire::QuantitySpec {
base_unit: U::base_unit().to_string(),
allowed_suffixes: U::allowed_suffixes()
.iter()
.map(|value| (*value).to_string())
.collect(),
min: None,
max: None,
}))
}
}
impl<U: QuantityUnit> IntoWire for Quantity<U> {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let value = self.as_quantity_value();
Ok(writer.push(wire::SchemaValueNode::QuantityValueNode(
wire::QuantityValue {
mantissa: value.mantissa,
scale: value.scale,
unit: value.unit.clone(),
},
)))
}
}
impl<U: QuantityUnit> FromWire for Quantity<U> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
let wire::SchemaValueNode::QuantityValueNode(value) = reader.take(index)? else {
return Err(WireError::Shape("quantity"));
};
Quantity::from_quantity_value(QuantityValue {
mantissa: value.mantissa,
scale: value.scale,
unit: value.unit,
})
.map_err(|_| WireError::Shape("quantity unit"))
}
}
impl WireSchema for std::path::PathBuf {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::PathType(wire::PathSpec {
direction: wire::PathDirection::InOut,
kind: wire::PathKind::Any,
allowed_mime_types: None,
allowed_extensions: None,
}))
}
}
impl FromWire for std::path::PathBuf {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::PathValue(path) => Ok(path.into()),
_ => Err(WireError::Shape("path")),
}
}
}
impl IntoWire for std::path::PathBuf {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::PathValue(
self.to_string_lossy().into_owned(),
)))
}
}
macro_rules! wire_schema_pointer {
($pointer:ty) => {
impl<T: WireSchema + ?Sized> WireSchema for $pointer {
const IS_UNIT: bool = T::IS_UNIT;
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
T::contains_stream(seen)
}
fn wire_type_id() -> String {
T::wire_type_id()
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
T::append_schema(builder)
}
}
};
}
wire_schema_pointer!(Box<T>);
wire_schema_pointer!(Rc<T>);
wire_schema_pointer!(std::sync::Arc<T>);
wire_schema_pointer!(&T);
impl<T: WireSchema> WireSchema for Vec<T> {
fn wire_type_id() -> String {
format!("alloc.vec.Vec<{}>", T::wire_type_id())
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
T::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let element = T::append_schema(builder);
builder.push(wire::SchemaTypeBody::ListType(element))
}
}
impl<T: WireSchema> WireSchema for HashSet<T> {
fn wire_type_id() -> String {
format!("std.collections.HashSet<{}>", T::wire_type_id())
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
T::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let element = T::append_schema(builder);
builder.push(wire::SchemaTypeBody::ListType(element))
}
}
impl<T: WireSchema> WireSchema for Option<T> {
fn wire_type_id() -> String {
format!("core.option.Option<{}>", T::wire_type_id())
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
T::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let inner = T::append_schema(builder);
builder.push(wire::SchemaTypeBody::OptionType(inner))
}
}
impl<T: WireSchema, E: WireSchema> WireSchema for Result<T, E> {
fn wire_type_id() -> String {
format!(
"core.result.Result<{}, {}>",
T::wire_type_id(),
E::wire_type_id()
)
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
T::contains_stream(seen) || E::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let ok = if T::IS_UNIT {
None
} else {
Some(T::append_schema(builder))
};
let err = if E::IS_UNIT {
None
} else {
Some(E::append_schema(builder))
};
builder.push(wire::SchemaTypeBody::ResultType(wire::ResultSpec {
ok,
err,
}))
}
}
impl WireSchema for () {
const IS_UNIT: bool = true;
fn wire_type_id() -> String {
"core.tuple.Unit".into()
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::TupleType(Vec::new()))
}
}
impl<K: WireSchema, V: WireSchema> WireSchema for HashMap<K, V> {
fn wire_type_id() -> String {
format!(
"std.collections.HashMap<{}, {}>",
K::wire_type_id(),
V::wire_type_id()
)
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
K::contains_stream(seen) || V::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let key = K::append_schema(builder);
let value = V::append_schema(builder);
builder.push(wire::SchemaTypeBody::MapType(wire::MapSpec { key, value }))
}
}
impl<K: WireSchema, V: WireSchema> WireSchema for BTreeMap<K, V> {
fn wire_type_id() -> String {
format!(
"alloc.collections.BTreeMap<{}, {}>",
K::wire_type_id(),
V::wire_type_id()
)
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
K::contains_stream(seen) || V::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let key = K::append_schema(builder);
let value = V::append_schema(builder);
builder.push(wire::SchemaTypeBody::MapType(wire::MapSpec { key, value }))
}
}
macro_rules! wire_map {
($map:ident, $($key_bound:tt)+) => {
impl<K: IntoWire, V: IntoWire> IntoWire for $map<K, V> {
const NEEDS_PREPARATION: bool = K::NEEDS_PREPARATION || V::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
for (key, value) in self {
key.preflight(resources)?;
value.preflight(resources)?;
}
Ok(())
}
async fn prepare_wire(&self) -> Result<(), WireError> {
for (key, value) in self {
key.prepare_wire().await?;
value.prepare_wire().await?;
}
Ok(())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let entries = self
.iter()
.map(|(key, value)| {
Ok(wire::MapEntry {
key: key.write_wire(writer)?,
value: value.write_wire(writer)?,
})
})
.collect::<Result<_, WireError>>()?;
Ok(writer.push(wire::SchemaValueNode::MapValue(entries)))
}
}
impl<K: FromWire + $($key_bound)+, V: FromWire> FromWire for $map<K, V> {
fn read_wire(
reader: &mut WireReader,
index: ValueNodeIndex,
) -> Result<Self, WireError> {
let wire::SchemaValueNode::MapValue(entries) = reader.take(index)? else {
return Err(WireError::Shape("map"));
};
entries
.into_iter()
.map(|entry| {
Ok((
K::read_wire(reader, entry.key)?,
V::read_wire(reader, entry.value)?,
))
})
.collect()
}
}
};
}
wire_map!(BTreeMap, Ord);
wire_map!(HashMap, Eq + std::hash::Hash);
impl<T: WireSchema> WireSchema for std::ops::Bound<T> {
fn wire_type_id() -> String {
format!("core.ops.range.Bound<{}>", T::wire_type_id())
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
T::contains_stream(seen)
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let included = T::append_schema(builder);
let excluded = T::append_schema(builder);
builder.push(wire::SchemaTypeBody::VariantType(vec![
wire::VariantCaseType {
name: "included".to_string(),
payload: Some(included),
metadata: empty_metadata(),
},
wire::VariantCaseType {
name: "excluded".to_string(),
payload: Some(excluded),
metadata: empty_metadata(),
},
wire::VariantCaseType {
name: "unbounded".to_string(),
payload: None,
metadata: empty_metadata(),
},
]))
}
}
macro_rules! wire_schema_tuple {
($($ty:ident),+) => { impl<$($ty: WireSchema),+> WireSchema for ($($ty,)+) {
fn wire_type_id() -> String {
let args = [$($ty::wire_type_id()),+];
format!("core.tuple.Tuple{}<{}>", args.len(), args.join(", "))
}
fn contains_stream(seen: &mut HashSet<&'static str>) -> bool {
false $(|| $ty::contains_stream(seen))+
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let elements = vec![$($ty::append_schema(builder)),+];
builder.push(wire::SchemaTypeBody::TupleType(elements))
}
}};
}
wire_schema_tuple!(A);
wire_schema_tuple!(A, B);
wire_schema_tuple!(A, B, C);
wire_schema_tuple!(A, B, C, D);
wire_schema_tuple!(A, B, C, D, E);
wire_schema_tuple!(A, B, C, D, E, F);
wire_schema_tuple!(A, B, C, D, E, F, G);
wire_schema_tuple!(A, B, C, D, E, F, G, H);
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum WireError {
#[error("expected wire {0}")]
Shape(&'static str),
#[error("wire value index {0} is out of bounds")]
OutOfBounds(ValueNodeIndex),
#[error("wire value index {0} is referenced more than once")]
AliasedNode(ValueNodeIndex),
#[error("wire resource at index {0} is not reachable from the root")]
UnreachableResource(ValueNodeIndex),
#[error("{0} resource appears more than once")]
AliasedResource(&'static str),
#[error("{0} resource has already been transferred")]
ConsumedResource(&'static str),
#[error("{0} resource is not allowed in this input")]
ForbiddenResource(&'static str),
#[error("native streams require asynchronous wire encoding")]
AsyncStream,
}
pub trait FromWire: Sized {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError>;
fn read_result_payload(
reader: &mut WireReader,
index: Option<ValueNodeIndex>,
) -> Result<Self, WireError> {
Self::read_wire(reader, index.ok_or(WireError::Shape("result payload"))?)
}
}
#[allow(async_fn_in_trait)]
pub trait IntoWire {
const NEEDS_PREPARATION: bool = true;
fn preflight(&self, _resources: &mut WirePreflight) -> Result<(), WireError> {
Ok(())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError>;
async fn prepare_wire(&self) -> Result<(), WireError> {
Ok(())
}
fn write_result_payload(
&self,
writer: &mut WireWriter,
) -> Result<Option<ValueNodeIndex>, WireError> {
self.write_wire(writer).map(Some)
}
}
enum SnapshotNode {
Value(wire::SchemaValueNode),
Secret(GuestSecretHandle),
QuotaToken(GuestQuotaTokenHandle),
PermissionCard(GuestPermissionCardHandle),
Stream(SchemaValueStream),
}
pub struct WireSnapshot {
nodes: Vec<SnapshotNode>,
}
impl WireSnapshot {
pub fn new(nodes: Vec<wire::SchemaValueNode>) -> Self {
let nodes = nodes
.into_iter()
.map(|node| match node {
wire::SchemaValueNode::SecretValue(value) => {
SnapshotNode::Secret(GuestSecretHandle::new(value))
}
wire::SchemaValueNode::QuotaTokenHandle(value) => {
SnapshotNode::QuotaToken(GuestQuotaTokenHandle::new(value))
}
wire::SchemaValueNode::PermissionCardHandle(value) => {
SnapshotNode::PermissionCard(GuestPermissionCardHandle::new(value))
}
wire::SchemaValueNode::StreamValue(value) => {
SnapshotNode::Stream(SchemaValueStream::from_wrapped(value))
}
value => SnapshotNode::Value(value),
})
.collect();
Self { nodes }
}
pub fn reader(self: &Rc<Self>) -> WireReader {
WireReader {
visited: vec![false; self.nodes.len()],
backing: ReaderBacking::Shared(Rc::clone(self)),
}
}
}
enum ReaderBacking {
Owned(Vec<Option<SnapshotNode>>),
Shared(Rc<WireSnapshot>),
}
pub struct WireReader {
backing: ReaderBacking,
visited: Vec<bool>,
}
impl WireReader {
pub fn new(nodes: Vec<wire::SchemaValueNode>) -> Self {
Self {
backing: ReaderBacking::Owned(
WireSnapshot::new(nodes)
.nodes
.into_iter()
.map(Some)
.collect(),
),
visited: Vec::new(),
}
}
pub fn take(&mut self, index: ValueNodeIndex) -> Result<wire::SchemaValueNode, WireError> {
let node = self.visit(index)?;
match node {
SnapshotNode::Value(value) => Ok(value),
_ => Err(WireError::Shape("non-resource value")),
}
}
pub fn push(&mut self, node: wire::SchemaValueNode) -> Result<ValueNodeIndex, WireError> {
let ReaderBacking::Owned(nodes) = &mut self.backing else {
return Err(WireError::Shape("cannot extend a shared wire reader"));
};
let index = nodes.len() as ValueNodeIndex;
nodes.push(Some(SnapshotNode::Value(node)));
Ok(index)
}
fn visit(&mut self, index: ValueNodeIndex) -> Result<SnapshotNode, WireError> {
let snapshot = match &mut self.backing {
ReaderBacking::Owned(nodes) => {
return nodes
.get_mut(index as usize)
.ok_or(WireError::OutOfBounds(index))?
.take()
.ok_or(WireError::AliasedNode(index));
}
ReaderBacking::Shared(snapshot) => snapshot,
};
let visited = self
.visited
.get_mut(index as usize)
.ok_or(WireError::OutOfBounds(index))?;
if std::mem::replace(visited, true) {
return Err(WireError::AliasedNode(index));
}
Ok(match &snapshot.nodes[index as usize] {
SnapshotNode::Value(value) => SnapshotNode::Value(clone_value_node(value)),
SnapshotNode::Secret(value) => SnapshotNode::Secret(value.clone()),
SnapshotNode::QuotaToken(value) => SnapshotNode::QuotaToken(value.clone()),
SnapshotNode::PermissionCard(value) => SnapshotNode::PermissionCard(value.clone()),
SnapshotNode::Stream(value) => SnapshotNode::Stream(value.clone()),
})
}
fn secret(&mut self, index: ValueNodeIndex) -> Result<GuestSecretHandle, WireError> {
match self.visit(index)? {
SnapshotNode::Secret(value) => Ok(value.clone()),
_ => Err(WireError::Shape("secret")),
}
}
fn quota_token(&mut self, index: ValueNodeIndex) -> Result<GuestQuotaTokenHandle, WireError> {
match self.visit(index)? {
SnapshotNode::QuotaToken(value) => Ok(value.clone()),
_ => Err(WireError::Shape("quota-token")),
}
}
fn permission_card(
&mut self,
index: ValueNodeIndex,
) -> Result<GuestPermissionCardHandle, WireError> {
match self.visit(index)? {
SnapshotNode::PermissionCard(value) => Ok(value.clone()),
_ => Err(WireError::Shape("permission-card")),
}
}
fn stream(&mut self, index: ValueNodeIndex) -> Result<SchemaValueStream, WireError> {
match self.visit(index)? {
SnapshotNode::Stream(value) => Ok(value.clone()),
_ => Err(WireError::Shape("stream")),
}
}
pub fn discard(&mut self, index: ValueNodeIndex) -> Result<(), WireError> {
let mut pending = vec![index];
while let Some(index) = pending.pop() {
let SnapshotNode::Value(node) = self.visit(index)? else {
continue;
};
match node {
wire::SchemaValueNode::RecordValue(children)
| wire::SchemaValueNode::TupleValue(children)
| wire::SchemaValueNode::ListValue(children)
| wire::SchemaValueNode::FixedListValue(children) => pending.extend(children),
wire::SchemaValueNode::MapValue(entries) => {
for entry in entries {
pending.extend([entry.key, entry.value]);
}
}
wire::SchemaValueNode::VariantValue(value) => pending.extend(value.payload),
wire::SchemaValueNode::OptionValue(value) => pending.extend(value),
wire::SchemaValueNode::ResultValue(value) => match value {
wire::ResultValuePayload::OkValue(value)
| wire::ResultValuePayload::ErrValue(value) => pending.extend(value),
},
wire::SchemaValueNode::UnionValue(value) => pending.push(value.body),
_ => {}
}
}
Ok(())
}
pub fn finish(self) -> Result<(), WireError> {
match self.backing {
ReaderBacking::Owned(nodes) => {
for (index, node) in nodes.iter().enumerate() {
if node
.as_ref()
.is_some_and(|node| !matches!(node, SnapshotNode::Value(_)))
{
return Err(WireError::UnreachableResource(index as ValueNodeIndex));
}
}
}
ReaderBacking::Shared(snapshot) => {
for (index, (node, visited)) in
snapshot.nodes.iter().zip(self.visited.iter()).enumerate()
{
if !visited && !matches!(node, SnapshotNode::Value(_)) {
return Err(WireError::UnreachableResource(index as ValueNodeIndex));
}
}
}
}
Ok(())
}
}
fn clone_value_node(node: &wire::SchemaValueNode) -> wire::SchemaValueNode {
match node {
wire::SchemaValueNode::BoolValue(v) => wire::SchemaValueNode::BoolValue(*v),
wire::SchemaValueNode::S8Value(v) => wire::SchemaValueNode::S8Value(*v),
wire::SchemaValueNode::S16Value(v) => wire::SchemaValueNode::S16Value(*v),
wire::SchemaValueNode::S32Value(v) => wire::SchemaValueNode::S32Value(*v),
wire::SchemaValueNode::S64Value(v) => wire::SchemaValueNode::S64Value(*v),
wire::SchemaValueNode::U8Value(v) => wire::SchemaValueNode::U8Value(*v),
wire::SchemaValueNode::U16Value(v) => wire::SchemaValueNode::U16Value(*v),
wire::SchemaValueNode::U32Value(v) => wire::SchemaValueNode::U32Value(*v),
wire::SchemaValueNode::U64Value(v) => wire::SchemaValueNode::U64Value(*v),
wire::SchemaValueNode::F32Value(v) => wire::SchemaValueNode::F32Value(*v),
wire::SchemaValueNode::F64Value(v) => wire::SchemaValueNode::F64Value(*v),
wire::SchemaValueNode::CharValue(v) => wire::SchemaValueNode::CharValue(*v),
wire::SchemaValueNode::StringValue(v) => wire::SchemaValueNode::StringValue(v.clone()),
wire::SchemaValueNode::RecordValue(v) => wire::SchemaValueNode::RecordValue(v.clone()),
wire::SchemaValueNode::VariantValue(v) => wire::SchemaValueNode::VariantValue(*v),
wire::SchemaValueNode::EnumValue(v) => wire::SchemaValueNode::EnumValue(*v),
wire::SchemaValueNode::FlagsValue(v) => wire::SchemaValueNode::FlagsValue(v.clone()),
wire::SchemaValueNode::TupleValue(v) => wire::SchemaValueNode::TupleValue(v.clone()),
wire::SchemaValueNode::ListValue(v) => wire::SchemaValueNode::ListValue(v.clone()),
wire::SchemaValueNode::FixedListValue(v) => {
wire::SchemaValueNode::FixedListValue(v.clone())
}
wire::SchemaValueNode::MapValue(v) => wire::SchemaValueNode::MapValue(v.clone()),
wire::SchemaValueNode::OptionValue(v) => wire::SchemaValueNode::OptionValue(*v),
wire::SchemaValueNode::ResultValue(v) => wire::SchemaValueNode::ResultValue(*v),
wire::SchemaValueNode::TextValue(v) => wire::SchemaValueNode::TextValue(v.clone()),
wire::SchemaValueNode::BinaryValue(v) => wire::SchemaValueNode::BinaryValue(v.clone()),
wire::SchemaValueNode::PathValue(v) => wire::SchemaValueNode::PathValue(v.clone()),
wire::SchemaValueNode::UrlValue(v) => wire::SchemaValueNode::UrlValue(v.clone()),
wire::SchemaValueNode::UuidValue(v) => wire::SchemaValueNode::UuidValue(*v),
wire::SchemaValueNode::DatetimeValue(v) => wire::SchemaValueNode::DatetimeValue(*v),
wire::SchemaValueNode::DurationValue(v) => wire::SchemaValueNode::DurationValue(*v),
wire::SchemaValueNode::QuantityValueNode(v) => {
wire::SchemaValueNode::QuantityValueNode(v.clone())
}
wire::SchemaValueNode::UnionValue(v) => wire::SchemaValueNode::UnionValue(v.clone()),
wire::SchemaValueNode::SecretValue(_)
| wire::SchemaValueNode::QuotaTokenHandle(_)
| wire::SchemaValueNode::PermissionCardHandle(_)
| wire::SchemaValueNode::StreamValue(_) => {
unreachable!("resources are snapshot separately")
}
}
}
pub fn decode<T: FromWire>(tree: wire::SchemaValueTree) -> Result<T, WireError> {
let mut reader = WireReader::new(tree.value_nodes);
let value = T::read_wire(&mut reader, tree.root)?;
reader.finish()?;
Ok(value)
}
pub fn decode_result_payload<T: FromWire>(
tree: Option<wire::SchemaValueTree>,
) -> Result<T, WireError> {
let (nodes, root) = match tree {
Some(tree) => (tree.value_nodes, Some(tree.root)),
None => (Vec::new(), None),
};
let mut reader = WireReader::new(nodes);
let value = T::read_result_payload(&mut reader, root)?;
reader.finish()?;
Ok(value)
}
#[derive(Default)]
pub struct WireWriter {
nodes: Vec<wire::SchemaValueNode>,
}
impl WireWriter {
pub fn push(&mut self, node: wire::SchemaValueNode) -> ValueNodeIndex {
let index = self.nodes.len() as ValueNodeIndex;
self.nodes.push(node);
index
}
pub fn finish(self, root: ValueNodeIndex) -> wire::SchemaValueTree {
wire::SchemaValueTree {
value_nodes: self.nodes,
root,
}
}
}
#[derive(Default)]
pub struct WirePreflight {
seen: HashSet<(&'static str, *const ())>,
allow_native_streams: bool,
}
impl WirePreflight {
pub fn asynchronous() -> Self {
Self {
allow_native_streams: true,
..Self::default()
}
}
pub fn reject_quota_tokens(&self) -> Result<(), WireError> {
if self.seen.iter().any(|(kind, _)| *kind == "quota-token") {
Err(WireError::ForbiddenResource("quota-token"))
} else {
Ok(())
}
}
fn resource(
&mut self,
kind: &'static str,
id: *const (),
present: bool,
) -> Result<(), WireError> {
if !present {
return Err(WireError::ConsumedResource(kind));
}
if !self.seen.insert((kind, id)) {
return Err(WireError::AliasedResource(kind));
}
Ok(())
}
}
pub fn encode<T: IntoWire + ?Sized>(value: &T) -> Result<wire::SchemaValueTree, WireError> {
let mut preflight = WirePreflight::default();
value.preflight(&mut preflight)?;
write(value)
}
pub async fn encode_async<T: IntoWire + ?Sized>(
value: &T,
) -> Result<wire::SchemaValueTree, WireError> {
let mut preflight = WirePreflight::asynchronous();
value.preflight(&mut preflight)?;
if T::NEEDS_PREPARATION {
value.prepare_wire().await?;
}
write(value)
}
fn write<T: IntoWire + ?Sized>(value: &T) -> Result<wire::SchemaValueTree, WireError> {
let mut writer = WireWriter::default();
let root = value.write_wire(&mut writer)?;
Ok(writer.finish(root))
}
macro_rules! scalar {
($($ty:ty => $variant:ident),* $(,)?) => {$(
impl FromWire for $ty {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::$variant(value) => Ok(value),
_ => Err(WireError::Shape(stringify!($variant))),
}
}
}
impl IntoWire for $ty {
const NEEDS_PREPARATION: bool = false;
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::$variant(self.clone())))
}
}
)*};
}
scalar! {
bool => BoolValue, i8 => S8Value, i16 => S16Value, i32 => S32Value,
i64 => S64Value, u8 => U8Value, u16 => U16Value, u32 => U32Value,
u64 => U64Value, f32 => F32Value, f64 => F64Value, char => CharValue,
}
#[cfg(any(
feature = "bigdecimal",
feature = "rust_decimal",
feature = "num_bigint",
feature = "serde_json_types",
feature = "chrono"
))]
macro_rules! wire_string {
($ty:ty, $encode:expr, $decode:expr) => {
impl $crate::schema::wit::direct::WireSchema for $ty {
fn wire_type_id() -> String {
<Self as $crate::schema::IntoSchema>::type_id().0
}
fn append_schema(builder: &mut $crate::schema::wit::direct::WireSchemaBuilder) -> i32 {
builder.push($crate::schema::wit::wire::SchemaTypeBody::StringType)
}
}
impl $crate::schema::wit::direct::IntoWire for $ty {
const NEEDS_PREPARATION: bool = false;
fn write_wire(
&self,
writer: &mut $crate::schema::wit::direct::WireWriter,
) -> Result<i32, $crate::schema::wit::direct::WireError> {
Ok(
writer.push($crate::schema::wit::wire::SchemaValueNode::StringValue(
($encode)(self),
)),
)
}
}
impl $crate::schema::wit::direct::FromWire for $ty {
fn read_wire(
reader: &mut $crate::schema::wit::direct::WireReader,
index: i32,
) -> Result<Self, $crate::schema::wit::direct::WireError> {
let $crate::schema::wit::wire::SchemaValueNode::StringValue(value) =
reader.take(index)?
else {
return Err($crate::schema::wit::direct::WireError::Shape("string"));
};
($decode)(&value)
.map_err(|_| $crate::schema::wit::direct::WireError::Shape(stringify!($ty)))
}
}
};
}
#[cfg(any(
feature = "bigdecimal",
feature = "rust_decimal",
feature = "num_bigint",
feature = "serde_json_types",
feature = "chrono"
))]
pub(crate) use wire_string;
impl FromWire for String {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::StringValue(value) => Ok(value),
wire::SchemaValueNode::TextValue(value) => Ok(value.text),
_ => Err(WireError::Shape("string or text")),
}
}
}
impl IntoWire for String {
const NEEDS_PREPARATION: bool = false;
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
self.as_str().write_wire(writer)
}
}
impl WireSchema for usize {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
<u64 as WireSchema>::append_schema(builder)
}
}
impl IntoWire for usize {
const NEEDS_PREPARATION: bool = false;
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
(*self as u64).write_wire(writer)
}
}
impl FromWire for usize {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
usize::try_from(u64::read_wire(reader, index)?).map_err(|_| WireError::Shape("usize range"))
}
}
impl IntoWire for str {
const NEEDS_PREPARATION: bool = false;
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::StringValue(self.to_string())))
}
}
macro_rules! wire_pointer {
($pointer:ty) => {
impl<T: IntoWire + ?Sized> IntoWire for $pointer {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
(**self).preflight(resources)
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
(**self).write_wire(writer)
}
async fn prepare_wire(&self) -> Result<(), WireError> {
if T::NEEDS_PREPARATION {
Box::pin((**self).prepare_wire()).await?;
}
Ok(())
}
fn write_result_payload(
&self,
writer: &mut WireWriter,
) -> Result<Option<ValueNodeIndex>, WireError> {
(**self).write_result_payload(writer)
}
}
impl<T: FromWire> FromWire for $pointer {
fn read_wire(
reader: &mut WireReader,
index: ValueNodeIndex,
) -> Result<Self, WireError> {
T::read_wire(reader, index).map(Self::new)
}
fn read_result_payload(
reader: &mut WireReader,
index: Option<ValueNodeIndex>,
) -> Result<Self, WireError> {
T::read_result_payload(reader, index).map(Self::new)
}
}
};
}
wire_pointer!(Box<T>);
wire_pointer!(Rc<T>);
wire_pointer!(std::sync::Arc<T>);
impl<T: IntoWire + ?Sized> IntoWire for &T {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
(**self).preflight(resources)
}
async fn prepare_wire(&self) -> Result<(), WireError> {
(**self).prepare_wire().await
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
(**self).write_wire(writer)
}
fn write_result_payload(
&self,
writer: &mut WireWriter,
) -> Result<Option<ValueNodeIndex>, WireError> {
(**self).write_result_payload(writer)
}
}
impl<T: IntoWire> IntoWire for Vec<T> {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
for value in self {
value.preflight(resources)?;
}
Ok(())
}
async fn prepare_wire(&self) -> Result<(), WireError> {
if T::NEEDS_PREPARATION {
for value in self {
Box::pin(value.prepare_wire()).await?;
}
}
Ok(())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let indices = self
.iter()
.map(|value| value.write_wire(writer))
.collect::<Result<_, _>>()?;
Ok(writer.push(wire::SchemaValueNode::ListValue(indices)))
}
}
impl<T: FromWire> FromWire for Vec<T> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::ListValue(indices) => indices
.into_iter()
.map(|index| T::read_wire(reader, index))
.collect(),
_ => Err(WireError::Shape("list")),
}
}
}
impl<T: IntoWire> IntoWire for HashSet<T> {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
for value in self {
value.preflight(resources)?;
}
Ok(())
}
async fn prepare_wire(&self) -> Result<(), WireError> {
if T::NEEDS_PREPARATION {
for value in self {
Box::pin(value.prepare_wire()).await?;
}
}
Ok(())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let indices = self
.iter()
.map(|value| value.write_wire(writer))
.collect::<Result<_, _>>()?;
Ok(writer.push(wire::SchemaValueNode::ListValue(indices)))
}
}
impl<T: FromWire + Eq + std::hash::Hash> FromWire for HashSet<T> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::ListValue(indices) => indices
.into_iter()
.map(|index| T::read_wire(reader, index))
.collect(),
_ => Err(WireError::Shape("list")),
}
}
}
impl<T: IntoWire> IntoWire for Option<T> {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
if let Some(value) = self {
value.preflight(resources)?;
}
Ok(())
}
async fn prepare_wire(&self) -> Result<(), WireError> {
if let Some(value) = self {
value.prepare_wire().await?;
}
Ok(())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let index = self
.as_ref()
.map(|value| value.write_wire(writer))
.transpose()?;
Ok(writer.push(wire::SchemaValueNode::OptionValue(index)))
}
}
impl<T: FromWire> FromWire for Option<T> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::OptionValue(index) => {
index.map(|index| T::read_wire(reader, index)).transpose()
}
_ => Err(WireError::Shape("option")),
}
}
}
impl<T: IntoWire, E: IntoWire> IntoWire for Result<T, E> {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION || E::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
match self {
Ok(value) => value.preflight(resources),
Err(value) => value.preflight(resources),
}
}
async fn prepare_wire(&self) -> Result<(), WireError> {
match self {
Ok(value) => value.prepare_wire().await,
Err(value) => value.prepare_wire().await,
}
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let payload = match self {
Ok(value) => wire::ResultValuePayload::OkValue(value.write_result_payload(writer)?),
Err(value) => wire::ResultValuePayload::ErrValue(value.write_result_payload(writer)?),
};
Ok(writer.push(wire::SchemaValueNode::ResultValue(payload)))
}
}
impl<T: FromWire, E: FromWire> FromWire for Result<T, E> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::ResultValue(wire::ResultValuePayload::OkValue(index)) => {
T::read_result_payload(reader, index).map(Ok)
}
wire::SchemaValueNode::ResultValue(wire::ResultValuePayload::ErrValue(index)) => {
E::read_result_payload(reader, index).map(Err)
}
_ => Err(WireError::Shape("result")),
}
}
}
impl<T: IntoWire> IntoWire for std::ops::Bound<T> {
const NEEDS_PREPARATION: bool = T::NEEDS_PREPARATION;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
match self {
std::ops::Bound::Included(value) | std::ops::Bound::Excluded(value) => {
value.preflight(resources)
}
std::ops::Bound::Unbounded => Ok(()),
}
}
async fn prepare_wire(&self) -> Result<(), WireError> {
match self {
std::ops::Bound::Included(value) | std::ops::Bound::Excluded(value) => {
value.prepare_wire().await
}
std::ops::Bound::Unbounded => Ok(()),
}
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let (case, payload) = match self {
std::ops::Bound::Included(value) => (0, Some(value.write_wire(writer)?)),
std::ops::Bound::Excluded(value) => (1, Some(value.write_wire(writer)?)),
std::ops::Bound::Unbounded => (2, None),
};
Ok(writer.push(wire::SchemaValueNode::VariantValue(
wire::VariantValuePayload { case, payload },
)))
}
}
impl<T: FromWire> FromWire for std::ops::Bound<T> {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
let wire::SchemaValueNode::VariantValue(value) = reader.take(index)? else {
return Err(WireError::Shape("bound variant"));
};
match (value.case, value.payload) {
(0, Some(index)) => T::read_wire(reader, index).map(std::ops::Bound::Included),
(1, Some(index)) => T::read_wire(reader, index).map(std::ops::Bound::Excluded),
(2, None) => Ok(std::ops::Bound::Unbounded),
_ => Err(WireError::Shape("bound case")),
}
}
}
impl IntoWire for () {
const NEEDS_PREPARATION: bool = false;
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::TupleValue(Vec::new())))
}
fn write_result_payload(
&self,
_writer: &mut WireWriter,
) -> Result<Option<ValueNodeIndex>, WireError> {
Ok(None)
}
}
impl FromWire for () {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::TupleValue(indices) if indices.is_empty() => Ok(()),
_ => Err(WireError::Shape("unit tuple")),
}
}
fn read_result_payload(
_reader: &mut WireReader,
index: Option<ValueNodeIndex>,
) -> Result<Self, WireError> {
match index {
Some(_) => Err(WireError::Shape("absent unit result payload")),
None => Ok(()),
}
}
}
macro_rules! tuple {
($($index:tt : $ty:ident),+) => {
impl<$($ty: IntoWire),+> IntoWire for ($($ty,)+) {
const NEEDS_PREPARATION: bool = false $(|| $ty::NEEDS_PREPARATION)+;
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
$(self.$index.preflight(resources)?;)+
Ok(())
}
async fn prepare_wire(&self) -> Result<(), WireError> {
$(self.$index.prepare_wire().await?;)+
Ok(())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let indices = vec![$(self.$index.write_wire(writer)?),+];
Ok(writer.push(wire::SchemaValueNode::TupleValue(indices)))
}
}
impl<$($ty: FromWire),+> FromWire for ($($ty,)+) {
#[allow(non_snake_case)]
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::TupleValue(indices) => {
let [$($ty),+] = indices.as_slice() else {
return Err(WireError::Shape("tuple arity"));
};
Ok(($($ty::read_wire(reader, *$ty)?,)+))
}
_ => Err(WireError::Shape("tuple")),
}
}
}
};
}
tuple!(0: A);
tuple!(0: A, 1: B);
tuple!(0: A, 1: B, 2: C);
tuple!(0: A, 1: B, 2: C, 3: D);
tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
macro_rules! resource {
($ty:ty, $method:ident, $variant:ident, $kind:literal) => {
impl FromWire for $ty {
fn read_wire(
reader: &mut WireReader,
index: ValueNodeIndex,
) -> Result<Self, WireError> {
reader.$method(index)
}
}
impl IntoWire for $ty {
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
resources.resource($kind, self.cell_id(), self.is_present())
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let handle = self.take().ok_or(WireError::ConsumedResource($kind))?;
Ok(writer.push(wire::SchemaValueNode::$variant(handle)))
}
}
};
}
resource!(GuestSecretHandle, secret, SecretValue, "secret");
resource!(
GuestQuotaTokenHandle,
quota_token,
QuotaTokenHandle,
"quota-token"
);
resource!(
GuestPermissionCardHandle,
permission_card,
PermissionCardHandle,
"permission-card"
);
impl WireSchema for GuestSecretHandle {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
let inner = <String as WireSchema>::append_schema(builder);
builder.push(wire::SchemaTypeBody::SecretType(wire::SecretSpec {
inner,
category: None,
}))
}
}
impl WireSchema for GuestQuotaTokenHandle {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::QuotaTokenType(wire::QuotaTokenSpec {
resource_name: None,
}))
}
}
impl WireSchema for GuestPermissionCardHandle {
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::PermissionCardType(
wire::PermissionCardSpec { polymorphic: false },
))
}
}
impl FromWire for SchemaValueStream {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
reader.stream(index)
}
}
impl IntoWire for SchemaValueStream {
fn preflight(&self, resources: &mut WirePreflight) -> Result<(), WireError> {
resources.resource("stream", self.cell_id(), self.is_present())?;
if !resources.allow_native_streams && !self.is_wrapped() {
return Err(WireError::AsyncStream);
}
Ok(())
}
async fn prepare_wire(&self) -> Result<(), WireError> {
self.ensure_wrapped()
.await
.map_err(|_| WireError::ConsumedResource("stream"))
}
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
let stream = self.take_wrapped().ok_or(WireError::AsyncStream)?;
Ok(writer.push(wire::SchemaValueNode::StreamValue(stream)))
}
}
impl WireSchema for SchemaValueStream {
fn contains_stream(_: &mut HashSet<&'static str>) -> bool {
true
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::StreamType(None))
}
}
#[cfg(feature = "bytes")]
impl WireSchema for bytes::Bytes {
fn wire_type_id() -> String {
"bytes.Bytes".to_string()
}
fn append_schema(builder: &mut WireSchemaBuilder) -> wire::TypeNodeIndex {
builder.push(wire::SchemaTypeBody::BinaryType(wire::BinaryRestrictions {
mime_types: None,
min_bytes: None,
max_bytes: None,
}))
}
}
#[cfg(feature = "bytes")]
impl IntoWire for bytes::Bytes {
fn write_wire(&self, writer: &mut WireWriter) -> Result<ValueNodeIndex, WireError> {
Ok(writer.push(wire::SchemaValueNode::BinaryValue(
wire::BinaryValuePayload {
bytes: self.to_vec(),
mime_type: None,
},
)))
}
}
#[cfg(feature = "bytes")]
impl FromWire for bytes::Bytes {
fn read_wire(reader: &mut WireReader, index: ValueNodeIndex) -> Result<Self, WireError> {
match reader.take(index)? {
wire::SchemaValueNode::BinaryValue(payload) => Ok(Self::from(payload.bytes)),
_ => Err(WireError::Shape("binary")),
}
}
}