1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This module provides an API for encoding and decoding FIDL transaction
//! messages directly, without using protocol bindings or Zircon channels.
//! The messages must be value types (no handles).
//!
//! # Usage
//!
//! ## Encoding
//!
//! 1. Create a header with `TransactionHeader::new`.
//! 2. Use one of the `encode_*` methods to encode the message.
//!
//! ## Decoding
//!
//! 1. Decode the header with `decode_transaction_header`.
//! 2. Use one of the `decode_*` methods to decode the message body.
//!
pub use fidl::encoding::{decode_transaction_header, DynamicFlags, TransactionHeader};
use fidl::{
encoding::{
Decoder, EmptyStruct, Encode, Encoder, Flexible, FlexibleResult, FlexibleResultType,
FlexibleType, FrameworkErr, ResultType, TransactionMessage, TransactionMessageType,
TypeMarker, ValueTypeMarker,
},
new_empty, Error, HandleDisposition,
};
/// A trait for types that can be a FIDL request/response body.
/// This is implemented for `()` and FIDL structs, tables, and unions.
pub use fidl::for_fidl_message_crate::Body;
/// A trait for types that can be the domain error variant of a FIDL response.
/// This only applies to two-way methods that use error syntax.
/// This is implemented for primitives and user-defined FIDL types.
pub use fidl::for_fidl_message_crate::ErrorType;
/// Encodes a FIDL transaction message (request or response).
/// Use this for one-way methods, events, and two-way method requests.
/// For two-way method responses:
/// - use `encode_response_result` if the method has error syntax (strict or flexible).
/// - use `encode_response_flexible` if the method is flexible (no error syntax).
/// - use this method otherwise.
pub fn encode_message<T: Body>(header: TransactionHeader, body: T) -> Result<Vec<u8>, Error> {
encode::<T::MarkerAtTopLevel>(header, T::MarkerAtTopLevel::borrow(&body))
}
/// Encodes a FIDL transaction response for a two-way method with error syntax.
/// Wraps the body in a result union set to ordinal 1 (Ok) or 2 (Err).
pub fn encode_response_result<T: Body, E: ErrorType>(
header: TransactionHeader,
result: Result<T, E>,
) -> Result<Vec<u8>, Error> {
encode::<FlexibleResultType<T::MarkerInResultUnion, E::Marker>>(
header,
FlexibleResult::new(match result {
Ok(ref body) => Ok(T::MarkerInResultUnion::borrow(body)),
Err(ref e) => Err(E::Marker::borrow(e)),
}),
)
}
/// Encodes a FIDL transaction response for a flexible two-way method without
/// error syntax. Wraps the body in a result union set to ordinal 1 (success).
pub fn encode_response_flexible<T: Body>(
header: TransactionHeader,
body: T,
) -> Result<Vec<u8>, Error> {
encode::<FlexibleType<T::MarkerInResultUnion>>(
header,
Flexible::Ok(T::MarkerInResultUnion::borrow(&body)),
)
}
/// Encodes a FIDL transaction response for a flexible two-way method,
/// for use in an open protocol when the method is unknown to the server.
pub fn encode_response_flexible_unknown(header: TransactionHeader) -> Result<Vec<u8>, Error> {
encode::<FlexibleType<EmptyStruct>>(
header,
Flexible::<()>::FrameworkErr(FrameworkErr::UnknownMethod),
)
}
/// Decodes a FIDL transaction message body (request or response).
/// Assumes `header` and `body` come from `decode_transaction_header`.
/// Use this for one-way methods, events, and two-way method requests.
/// For two-way method responses:
/// - use `decode_response_strict_result` if the method is strict and has error syntax.
/// - use `decode_response_flexible_result` if the method is flexible and has error syntax.
/// - use `decode_response_flexible` if the method is flexible (no error syntax).
/// - use this method otherwise.
pub fn decode_message<T: Body>(header: TransactionHeader, body: &[u8]) -> Result<T, Error> {
decode::<T::MarkerAtTopLevel>(header, body)
}
/// Decodes a FIDL response body for a flexible two-way method with error syntax.
/// Assumes `header` and `body` come from `decode_transaction_header`.
pub fn decode_response_strict_result<T: Body, E: ErrorType>(
header: TransactionHeader,
body: &[u8],
) -> Result<Result<T, E>, Error> {
decode::<ResultType<T::MarkerInResultUnion, E::Marker>>(header, body)
}
/// Return type for functions that decode flexible responses.
pub enum MaybeUnknown<T> {
/// The server replied normally.
Known(T),
/// The server did not recognize the method ordinal.
Unknown,
}
/// Decodes a FIDL response body for a flexible two-way method without error syntax.
/// Assumes `header` and `body` come from `decode_transaction_header`.
pub fn decode_response_flexible<T: Body>(
header: TransactionHeader,
body: &[u8],
) -> Result<MaybeUnknown<T>, Error> {
match decode::<FlexibleType<T::MarkerInResultUnion>>(header, body)? {
Flexible::Ok(value) => Ok(MaybeUnknown::Known(value)),
Flexible::FrameworkErr(err) => match err {
FrameworkErr::UnknownMethod => Ok(MaybeUnknown::Unknown),
},
}
}
/// Decodes a FIDL response body for a flexible two-way method with error syntax.
/// Assumes `header` and `body` come from `decode_transaction_header`.
pub fn decode_response_flexible_result<T: Body, E: ErrorType>(
header: TransactionHeader,
body: &[u8],
) -> Result<MaybeUnknown<Result<T, E>>, Error> {
match decode::<FlexibleResultType<T::MarkerInResultUnion, E::Marker>>(header, body)? {
FlexibleResult::Ok(value) => Ok(MaybeUnknown::Known(Ok(value))),
FlexibleResult::DomainErr(err) => Ok(MaybeUnknown::Known(Err(err))),
FlexibleResult::FrameworkErr(err) => match err {
FrameworkErr::UnknownMethod => Ok(MaybeUnknown::Unknown),
},
}
}
fn encode<T: TypeMarker>(
header: TransactionHeader,
body: impl Encode<T>,
) -> Result<Vec<u8>, Error> {
let msg = TransactionMessage { header, body };
let mut combined_bytes = Vec::<u8>::new();
let mut handles = Vec::<HandleDisposition<'static>>::new();
Encoder::encode::<TransactionMessageType<T>>(&mut combined_bytes, &mut handles, msg)?;
debug_assert!(handles.is_empty(), "value type contains handles");
Ok(combined_bytes)
}
fn decode<T: TypeMarker>(header: TransactionHeader, body: &[u8]) -> Result<T::Owned, Error> {
let mut output = new_empty!(T);
Decoder::decode_into::<T>(&header, body, &mut [], &mut output)?;
Ok(output)
}