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}