fidl_table_validation/
lib.rs

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright 2019 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.

//! fidl table validation tools.
//!
//! This crate's macro generates code to validate fidl tables.
//!
//! Import using `fidl_table_validation::*` to inherit the macro's imports.
//!
//! ## Basic Example
//!
//! ```
//! // Some fidl table defined somewhere...
//! struct FidlTable {
//!     required: Option<usize>,
//!     optional: Option<usize>,
//!     has_default: Option<usize>,
//! }
//!
//! #[derive(ValidFidlTable)]
//! #[fidl_table_src(FidlHello)]
//! struct ValidatedFidlTable {
//!     // The default is #[fidl_field_type(required)]
//!     required: usize,
//!     #[fidl_field_type(optional)]
//!     optional: Option<usize>,
//!     #[fidl_field_type(default = 22)]
//!     has_default: usize,
//! }
//! ```
//!
//! This code generates a [TryFrom][std::convert::TryFrom]<FidlTable>
//! implementation for `ValidatedFidlTable`:
//!
//! ```
//! pub enum FidlTableValidationError {
//!     MissingField(FidlTableMissingFieldError)
//! }
//!
//! impl TryFrom<FidlTable> for ValidatedFidlTable {
//!     type Error = FidlTableValidationError;
//!     fn try_from(src: FidlTable) -> Result<ValidatedFidlTable, Self::Error> { .. }
//! }
//! ```
//! and also a [From][std::convert::From]<ValidatedFidlTable> implementation for
//! `FidlTable`, so you can get a `FidlTable` using `validated.into()`.
//!
//! ## Custom Validations
//!
//! When tables have logical relationships between fields that must be checked,
//! you can use a custom validator:
//!
//! ```
//! struct FidlTableValidator;
//!
//! impl Validate<ValidatedFidlTable> for FidlTableValidator {
//!     type Error = String; // Can be any error type.
//!     fn validate(candidate: &ValidatedFidlTable) -> Result<(), Self::Error> {
//!         match candidate.required {
//!             10 => {
//!                 Err(String::from("10 is not a valid value!"))
//!             }
//!             _ => Ok(())
//!         }
//!     }
//! }
//!
//! #[fidl_table_src(FidlHello)]
//! #[fidl_table_validator(FidlTableValidator)]
//! struct ValidFidlTable {
//! ...
//! ```
//!
//! ## Non-literal defaults
//!
//! Attribute syntax for `name = value` only supports literals. Another
//! attribute for expressing defaults is used for consts. Or any type that has a
//! `Default` impl can simply omit the literal.
//!
//! ```
//! const MY_DEFAULT: MyEnum = MyEnum::MyVariant;
//!
//! #[derive(ValidFidlTable)]
//! #[fidl_table_src(FidlHello)]
//! struct ValidatedFidlTable {
//!     #[fidl_field_type(default)]
//!     has_default_impl: Vec<u32>,
//!     #[fidl_field_with_default(MY_DEFAULT)]
//!     has_default: MyEnum,
//! }
//! ```
//!
//! ## Custom converters
//!
//! Custom converters (implementing [`Converter`]) can be used to validate the
//! fields. A `converter` field type acts like `required` (i.e. the derive
//! handles the optionality) and passes the unwrapped option to the given
//! converter. An `optional_converter` field type acts like `optional` and the
//! converter is given (and expects to generate back) `Option` wrapped values.
//!
//! Example:
//!
//! ```
//! struct RequiredConverter;
//!
//! impl Converter for RequiredConverter {
//!     type Fidl = u32;
//!     type Validated = NonZeroU32;
//!     type Error = anyhow::Error;
//!     fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
//!        NonZeroU32::new(value).ok_or_else(|| anyhow::anyhow!("bad zero value"))
//!     }
//!     fn from_validated(validated: Self::Validated) -> Self::Fidl {
//!        validated.get()
//!     }
//! }
//!
//! struct OptionalConverter;
//!
//! enum MaybeMissing { Present(u32), Missing }
//!
//! impl Converter for OptionalConverter {
//!    type Fidl = Option<u32>;
//!    type Validated = MaybeMissing;
//!    type Error = std::convert::Infallible;
//!     fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
//!        Ok(match value {
//!           Some(v) => MaybeMissing::Present(v),
//!           None => MaybeMissing::Missing,
//!        })
//!     }
//!     fn from_validated(validated: Self::Validated) -> Self::Fidl {
//!        match validated {
//!          MaybeMissing::Present(v) => Some(v),
//!          MaybeMissing::Missing => None,
//!        }
//!     }
//! }
//!
//! #[derive(ValidFidlTable)]
//! #[fidl_table_src(FidlHello)]
//! #[fidl_table_strict]
//! struct ValidatedFidlTable {
//!     #[fidl_field_type(converter = RequiredConverter)]
//!     foo: NonZeroU32,
//!     #[fidl_field_type(optional_converter = OptionalConverter)]
//!     bar: MaybeMissing,
//! }
//!
//! ```
//!
//! ## Strict conversion
//!
//! By default, this derive does _not_ cause compilation errors if the source
//! FIDL table has more fields than the validated struct. This behavior can be
//! changed with the `fidl_table_strict` attribute. For example, the snippet
//! below fails to compile if `FidlHello` has more fields than the ones in
//! `ValidatedFidlTable`.
//!
//! ```
//! #[derive(ValidFidlTable)]
//! #[fidl_table_src(FidlHello)]
//! #[fidl_table_strict]
//! struct ValidatedFidlTable {
//!     hello: u32,
//! }
//! ```
//!
//! Fields from the FIDL table can be explicitly ignored by giving
//! `fidl_table_strict` arguments:
//!
//! ```
//! #[derive(ValidFidlTable)]
//! #[fidl_table_src(FidlHello)]
//! #[fidl_table_strict(world)]
//! struct ValidatedFidlTable {
//!     hello: u32,
//! }
//! ```
//!
//! This adds a `Logical(YourErrorType)` variant to the generated error enum.
// TODO(turnage): Infer optionality based on parsing for
//                "Option<" in field types.
pub use fidl_table_validation_derive::ValidFidlTable;

pub use anyhow;

/// Validations on `T` that can be run during construction of a validated
/// fidl table by adding the attribute `#[fidl_table_validator(YourImpl)]`
/// to the valid struct.
pub trait Validate<T> {
    type Error;
    fn validate(candidate: &T) -> std::result::Result<(), Self::Error>;
}

/// A converter trait that can convert from FIDL types `T` into a validated type
/// `Validated`.
///
/// Used in conjunction with `fidl_field_type` with custom converters.
pub trait Converter {
    type Fidl;
    type Validated;
    type Error;
    fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error>;
    fn from_validated(validated: Self::Validated) -> Self::Fidl;
}