fidl_message/
lib.rs

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