fidl_table_validation/
lib.rs

1// Copyright 2019 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//! fidl table validation tools.
6//!
7//! This crate's macro generates code to validate fidl tables.
8//!
9//! Import using `fidl_table_validation::*` to inherit the macro's imports.
10//!
11//! ## Basic Example
12//!
13//! ```
14//! // Some fidl table defined somewhere...
15//! struct FidlTable {
16//!     required: Option<usize>,
17//!     optional: Option<usize>,
18//!     has_default: Option<usize>,
19//! }
20//!
21//! #[derive(ValidFidlTable)]
22//! #[fidl_table_src(FidlHello)]
23//! struct ValidatedFidlTable {
24//!     // The default is #[fidl_field_type(required)]
25//!     required: usize,
26//!     #[fidl_field_type(optional)]
27//!     optional: Option<usize>,
28//!     #[fidl_field_type(default = 22)]
29//!     has_default: usize,
30//! }
31//! ```
32//!
33//! This code generates a [TryFrom][std::convert::TryFrom]<FidlTable>
34//! implementation for `ValidatedFidlTable`:
35//!
36//! ```
37//! pub enum FidlTableValidationError {
38//!     MissingField(FidlTableMissingFieldError)
39//! }
40//!
41//! impl TryFrom<FidlTable> for ValidatedFidlTable {
42//!     type Error = FidlTableValidationError;
43//!     fn try_from(src: FidlTable) -> Result<ValidatedFidlTable, Self::Error> { .. }
44//! }
45//! ```
46//! and also a [From][std::convert::From]<ValidatedFidlTable> implementation for
47//! `FidlTable`, so you can get a `FidlTable` using `validated.into()`.
48//!
49//! ## Custom Validations
50//!
51//! When tables have logical relationships between fields that must be checked,
52//! you can use a custom validator:
53//!
54//! ```
55//! struct FidlTableValidator;
56//!
57//! impl Validate<ValidatedFidlTable> for FidlTableValidator {
58//!     type Error = String; // Can be any error type.
59//!     fn validate(candidate: &ValidatedFidlTable) -> Result<(), Self::Error> {
60//!         match candidate.required {
61//!             10 => {
62//!                 Err(String::from("10 is not a valid value!"))
63//!             }
64//!             _ => Ok(())
65//!         }
66//!     }
67//! }
68//!
69//! #[fidl_table_src(FidlHello)]
70//! #[fidl_table_validator(FidlTableValidator)]
71//! struct ValidFidlTable {
72//! ...
73//! ```
74//!
75//! ## Non-literal defaults
76//!
77//! Attribute syntax for `name = value` only supports literals. Another
78//! attribute for expressing defaults is used for consts. Or any type that has a
79//! `Default` impl can simply omit the literal.
80//!
81//! ```
82//! const MY_DEFAULT: MyEnum = MyEnum::MyVariant;
83//!
84//! #[derive(ValidFidlTable)]
85//! #[fidl_table_src(FidlHello)]
86//! struct ValidatedFidlTable {
87//!     #[fidl_field_type(default)]
88//!     has_default_impl: Vec<u32>,
89//!     #[fidl_field_with_default(MY_DEFAULT)]
90//!     has_default: MyEnum,
91//! }
92//! ```
93//!
94//! ## Custom converters
95//!
96//! Custom converters (implementing [`Converter`]) can be used to validate the
97//! fields. A `converter` field type acts like `required` (i.e. the derive
98//! handles the optionality) and passes the unwrapped option to the given
99//! converter. An `optional_converter` field type acts like `optional` and the
100//! converter is given (and expects to generate back) `Option` wrapped values.
101//!
102//! Example:
103//!
104//! ```
105//! struct RequiredConverter;
106//!
107//! impl Converter for RequiredConverter {
108//!     type Fidl = u32;
109//!     type Validated = NonZeroU32;
110//!     type Error = anyhow::Error;
111//!     fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
112//!        NonZeroU32::new(value).ok_or_else(|| anyhow::anyhow!("bad zero value"))
113//!     }
114//!     fn from_validated(validated: Self::Validated) -> Self::Fidl {
115//!        validated.get()
116//!     }
117//! }
118//!
119//! struct OptionalConverter;
120//!
121//! enum MaybeMissing { Present(u32), Missing }
122//!
123//! impl Converter for OptionalConverter {
124//!    type Fidl = Option<u32>;
125//!    type Validated = MaybeMissing;
126//!    type Error = std::convert::Infallible;
127//!     fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
128//!        Ok(match value {
129//!           Some(v) => MaybeMissing::Present(v),
130//!           None => MaybeMissing::Missing,
131//!        })
132//!     }
133//!     fn from_validated(validated: Self::Validated) -> Self::Fidl {
134//!        match validated {
135//!          MaybeMissing::Present(v) => Some(v),
136//!          MaybeMissing::Missing => None,
137//!        }
138//!     }
139//! }
140//!
141//! #[derive(ValidFidlTable)]
142//! #[fidl_table_src(FidlHello)]
143//! #[fidl_table_strict]
144//! struct ValidatedFidlTable {
145//!     #[fidl_field_type(converter = RequiredConverter)]
146//!     foo: NonZeroU32,
147//!     #[fidl_field_type(optional_converter = OptionalConverter)]
148//!     bar: MaybeMissing,
149//! }
150//!
151//! ```
152//!
153//! ## Strict conversion
154//!
155//! By default, this derive does _not_ cause compilation errors if the source
156//! FIDL table has more fields than the validated struct. This behavior can be
157//! changed with the `fidl_table_strict` attribute. For example, the snippet
158//! below fails to compile if `FidlHello` has more fields than the ones in
159//! `ValidatedFidlTable`.
160//!
161//! ```
162//! #[derive(ValidFidlTable)]
163//! #[fidl_table_src(FidlHello)]
164//! #[fidl_table_strict]
165//! struct ValidatedFidlTable {
166//!     hello: u32,
167//! }
168//! ```
169//!
170//! Fields from the FIDL table can be explicitly ignored by giving
171//! `fidl_table_strict` arguments:
172//!
173//! ```
174//! #[derive(ValidFidlTable)]
175//! #[fidl_table_src(FidlHello)]
176//! #[fidl_table_strict(world)]
177//! struct ValidatedFidlTable {
178//!     hello: u32,
179//! }
180//! ```
181//!
182//! This adds a `Logical(YourErrorType)` variant to the generated error enum.
183// TODO(turnage): Infer optionality based on parsing for
184//                "Option<" in field types.
185pub use fidl_table_validation_derive::ValidFidlTable;
186
187pub use anyhow;
188
189/// Validations on `T` that can be run during construction of a validated
190/// fidl table by adding the attribute `#[fidl_table_validator(YourImpl)]`
191/// to the valid struct.
192pub trait Validate<T> {
193    type Error;
194    fn validate(candidate: &T) -> std::result::Result<(), Self::Error>;
195}
196
197/// A converter trait that can convert from FIDL types `T` into a validated type
198/// `Validated`.
199///
200/// Used in conjunction with `fidl_field_type` with custom converters.
201pub trait Converter {
202    type Fidl;
203    type Validated;
204    type Error;
205    fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error>;
206    fn from_validated(validated: Self::Validated) -> Self::Fidl;
207}