fiberplane_models/providers/schema/fields/
array_field.rs

1use crate::providers::QuerySchema;
2#[cfg(feature = "fp-bindgen")]
3use fp_bindgen::prelude::Serializable;
4use serde::{Deserialize, Serialize};
5
6/// Defines an array of composite fields.
7///
8/// This is commonly used for arbitrarily long list of (key, value) pairs,
9/// or lists of (key, operator, value) filters.
10#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
11#[cfg_attr(
12    feature = "fp-bindgen",
13    derive(Serializable),
14    fp(rust_module = "fiberplane_models::providers")
15)]
16#[non_exhaustive]
17#[serde(rename_all = "camelCase")]
18pub struct ArrayField {
19    /// Suggested label to display along the form field.
20    pub label: String,
21
22    /// Name of the field as it will be included in the encoded query or config
23    /// object.
24    pub name: String,
25
26    /// The minimum number of entries the array must have to be valid.
27    ///
28    /// Leaving the minimum_length to 0 makes the whole field optional.
29    pub minimum_length: u32,
30
31    /// The maximum number of entries the array can have and still be valid.
32    ///
33    /// It is None when there is no maximum number
34    #[serde(default, skip_serializing_if = "Option::is_none")]
35    pub maximum_length: Option<u32>,
36
37    /// The schema of the elements inside a row of the array.
38    ///
39    /// ### Accessing row fields
40    ///
41    /// The name of each QueryField inside the element_schema can be used as
42    /// an indexing key for a field. That means that if `element_schema` contains
43    /// a [TextField](crate::providers::TextField) with the name `parameter_name`,
44    /// then you will be able to access the value of that field using
45    /// `ArrayField::get(i)::get("parameter_name")` for the i-th element.
46    ///
47    /// ### Serialization
48    ///
49    /// For example if an array field has this `element_schema`:
50    /// ```rust,no_run
51    /// # use fiberplane_models::providers::{ArrayField, TextField, SelectField, IntegerField};
52    /// ArrayField::new()
53    ///   .with_name("table")
54    ///   .with_label("example".to_string())
55    ///   .with_element_schema(vec![
56    ///     TextField::new().with_name("key").into(),
57    ///     SelectField::new().with_name("operator").with_options(&["<", ">", "<=", ">=", "=="]).into(),
58    ///     IntegerField::new().with_name("value").into(),
59    ///   ]);
60    /// ```
61    ///
62    /// Then the URL-encoded serialization for the fields is expected to use
63    /// the bracketed-notation. This means you _can_ encode all the
64    /// keys in the array in any order you want. It can look like this
65    /// (line breaks are only kept for legibility):
66    /// ```txt
67    ///  "table[0][key]=less+than&
68    ///  table[2][operator]=%3E&
69    ///  table[0][operator]=%3C&
70    ///  table[2][key]=greater+than&
71    ///  table[2][value]=10&
72    ///  table[0][value]=12"
73    /// ```
74    ///
75    /// or you can do the "logic" ordering too:
76    /// ```txt
77    ///  "table[0][key]=less+than&
78    ///  table[0][operator]=%3C&
79    ///  table[0][value]=12&
80    ///  table[1][key]=greater+than&
81    ///  table[1][operator]=%3E&
82    ///  table[1][value]=10"
83    /// ```
84    ///
85    /// Note that we are allowed to skip indices.
86    /// Any of those 2 examples above will
87    /// be read as:
88    /// ```rust,no_run
89    /// # #[derive(Debug, PartialEq)]
90    /// # struct Row { key: String, operator: String, value: u32 }
91    /// # let table: Vec<Row> = vec![];
92    /// assert_eq!(table, vec![
93    ///   Row {
94    ///     key: "less than".to_string(),
95    ///     operator: "<".to_string(),
96    ///     value: 12,
97    ///   },
98    ///   Row {
99    ///     key: "greater than".to_string(),
100    ///     operator: ">".to_string(),
101    ///     value: 10,
102    ///   },
103    /// ]);
104    /// ```
105    ///
106    /// ### Required row fields
107    ///
108    /// Any field that is marked as `required` inside `element_schema` makes it
109    /// mandatory to create a valid row to the Array Field.
110    pub element_schema: QuerySchema,
111}
112
113impl ArrayField {
114    /// Creates a new array field with all default values.
115    pub fn new() -> Self {
116        Default::default()
117    }
118
119    pub fn with_label(self, label: impl Into<String>) -> Self {
120        Self {
121            label: label.into(),
122            ..self
123        }
124    }
125
126    pub fn with_element_schema(self, schema: QuerySchema) -> Self {
127        Self {
128            element_schema: schema,
129            ..self
130        }
131    }
132
133    pub fn with_name(self, name: impl Into<String>) -> Self {
134        Self {
135            name: name.into(),
136            ..self
137        }
138    }
139
140    pub fn with_minimum_length(self, minimum_length: u32) -> Self {
141        Self {
142            minimum_length,
143            ..self
144        }
145    }
146
147    pub fn with_maximum_length(self, maximum_length: u32) -> Self {
148        Self {
149            maximum_length: Some(maximum_length),
150            ..self
151        }
152    }
153}