Skip to main content

substrate_data/schema/
fields.rs

1//! A struct's fields as files write them, by their Rust names: code that
2//! names a field (where a problem is) says it through [`field!`](crate::field),
3//! checked when it's compiled and renamed with the field.
4
5/// A struct with named fields (`#[derive(Schema)]`): each field's Rust name
6/// and its name in files.
7pub trait Fields {
8    /// Each field's Rust name and the key it's written as.
9    const FIELDS: &'static [(&'static str, &'static str)];
10}
11
12/// `ident`'s name in files among `fields` — a compile error when it's used
13/// in a constant (as [`field!`](crate::field) does) and there's no such field.
14///
15/// # Panics
16///
17/// When `fields` has no `ident` — at compile time in a constant, which is
18/// how [`field!`](crate::field) turns a misspelt field into a compile error.
19#[must_use]
20#[expect(
21    clippy::panic,
22    reason = "a panic in a constant is the compile error `field!` gives"
23)]
24pub const fn field_name(
25    fields: &'static [(&'static str, &'static str)],
26    ident: &str,
27) -> &'static str {
28    let mut i = 0;
29    while i < fields.len() {
30        if same(fields[i].0, ident) {
31            return fields[i].1;
32        }
33        i += 1;
34    }
35    panic!("no such field")
36}
37
38const fn same(a: &str, b: &str) -> bool {
39    let (a, b) = (a.as_bytes(), b.as_bytes());
40    if a.len() != b.len() {
41        return false;
42    }
43    let mut i = 0;
44    while i < a.len() {
45        if a[i] != b[i] {
46            return false;
47        }
48        i += 1;
49    }
50    true
51}
52
53/// Field `ident` of struct `T` as files write it: `field!(UserProfile, display_name)`
54/// → `"display_name"`. A field `T` hasn't got doesn't compile.
55#[macro_export]
56macro_rules! field {
57    ($t:ty, $f:ident) => {{
58        const NAME: &str = $crate::schema::field_name(
59            <$t as $crate::schema::Fields>::FIELDS,
60            ::core::stringify!($f),
61        );
62        NAME
63    }};
64}
65
66#[cfg(test)]
67mod tests {
68    use crate::Schema;
69
70    #[derive(Schema)]
71    struct Probe {
72        plain: u32,
73        #[data(rename = "written")]
74        renamed: u32,
75    }
76
77    #[test]
78    fn fields_by_their_rust_names() {
79        assert_eq!(crate::field!(Probe, plain), "plain");
80        assert_eq!(crate::field!(Probe, renamed), "written");
81    }
82}