Last active
April 25, 2023 21:48
-
-
Save elycruz/d5d7bd801cefd5c6c7094aab9bc0763c to your computer and use it in GitHub Desktop.
Example of declaring a vector with a complex data type, with nested smart pointers, and generics.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use core::fmt::Formatter; | |
/** | |
* The goal of this example is to show a way to have algebraic types and generics | |
* represent a collection of structs that can each have their own types and | |
* additionally be structurally different from each other. | |
* | |
* Question: Are there less "typeful"/verbose ways of acheiving the same this? | |
*/ | |
use core::fmt::{Display, Debug}; | |
use std::borrow::Cow; | |
use std::mem::{size_of_val}; | |
use crate::FCVariant::{ | |
BoolInput, UsizeInput, StrInput, BoolButton, | |
UsizeButton, StrButton, UnitFieldset | |
}; | |
/// Empty type that complies to our `FormControlValue` (`FCValue`) type (below). | |
/// ---- | |
#[derive(Debug, Clone, PartialEq, PartialOrd)] | |
pub struct Null {} | |
impl Display for Null { | |
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
write!(f, "null") | |
} | |
} | |
/// Form Control Value - Should allow all scalar types/values - Can be set up | |
/// to support other types, but for example scalars will do just fine. | |
/// ---- | |
pub trait FCValue: Clone + Debug + Display + PartialEq + PartialOrd {} | |
/// All types we want to allow in our `Input`: | |
/// ---- | |
impl FCValue for bool {} | |
impl FCValue for Null {} | |
impl FCValue for usize {} | |
impl<'a> FCValue for &'a str {} | |
// ... | |
/// Trait for grouping common control methods: | |
/// ---- | |
pub trait FormControl<'a, T: FCValue + 'a> { | |
fn get_name(&self) -> Option<&Cow<'a, str>>; | |
fn set_value(&mut self, value: Option<Cow<'a, T>>); | |
fn get_value(&self) -> Option<&Cow<'a, T>>; | |
fn validate(&mut self, _value: Option<&Cow<'a, T>>) -> bool { | |
false | |
} | |
// ... | |
/// Example referencing `Cow<...>` values internally. | |
fn print_info(&self) { | |
println!("{:?}: {:?}; Size: {}", self.get_name(), self.get_value(), size_of_val(self)); | |
} | |
} | |
/// Form control variant types. | |
/// ---- | |
#[derive(Debug)] | |
pub enum FCVariant<'a> { | |
BoolInput(Input<'a, bool>), | |
UsizeInput(Input<'a, usize>), | |
StrInput(Input<'a, &'a str>), | |
BoolButton(Button<'a, bool>), | |
UsizeButton(Button<'a, usize>), | |
StrButton(Button<'a, &'a str>), | |
UnitFieldset(Fieldset<'a>) | |
} | |
/// Implement methods for common "subtype" methods | |
/// ---- | |
impl <'a> FCVariant<'a> { | |
/// Note: becomes exponentially larger the more types we support - Question: | |
/// Does this approach make sense when we have say 3 controls that support | |
/// 12 scalar types each? - Maybe we could use/create a derive macro to | |
/// autopopulate the methods? | |
pub fn print_info(&self) { | |
match self { | |
UsizeInput(inpt) => inpt.print_info(), | |
BoolInput(inpt) => inpt.print_info(), | |
StrInput(inpt) => inpt.print_info(), | |
UsizeButton(inpt) => inpt.print_info(), | |
BoolButton(inpt) => inpt.print_info(), | |
StrButton(inpt) => inpt.print_info(), | |
UnitFieldset(inpt) => { | |
inpt.print_info(); | |
inpt.elements.as_ref().map(|elms|{ | |
elms.iter().for_each(|elm| { | |
elm.print_info(); | |
}); | |
}); | |
} | |
} | |
} | |
} | |
/// Input control. | |
#[derive(Debug)] | |
pub struct Input<'a, T: FCValue> { | |
name: Option<Cow<'a, str>>, | |
value: Option<Cow<'a, T>> | |
} | |
/// Input impl. | |
impl<'a, T: FCValue + 'a> Input<'a, T> { | |
pub fn new(name: Option<Cow<'a, str>>, value: Option<Cow<'a, T>>) -> Self { | |
Input { | |
name, | |
value | |
} | |
} | |
} | |
/// Form control impl. for Input: | |
impl<'a, T: FCValue + 'a> FormControl<'a, T> for Input<'a, T> where | |
T: FCValue { | |
fn get_name(&self) -> Option<&Cow<'a, str>> { | |
self.name.as_ref() | |
} | |
fn set_value(&mut self, value: Option<Cow<'a, T>>) { | |
self.value = value; | |
} | |
fn get_value(&self) -> Option<&Cow<'a, T>> { | |
self.value.as_ref() | |
} | |
} | |
/// Button control. | |
#[derive(Debug)] | |
pub struct Button<'a, T: FCValue> { | |
name: Option<Cow<'a, str>>, | |
value: Option<Cow<'a, T>> | |
} | |
/// Button impl. | |
impl<'a, T: FCValue> Button<'a, T> { | |
pub fn new(name: Option<Cow<'a, str>>, value: Option<Cow<'a, T>>) -> Self { | |
Button { | |
name, | |
value | |
} | |
} | |
} | |
/// Form control impl. for Button: | |
impl<'a, T: FCValue + 'a> FormControl<'a, T> for Button<'a, T> where | |
T: FCValue { | |
fn get_name(&self) -> Option<&Cow<'a, str>> { | |
self.name.as_ref() | |
} | |
fn set_value(&mut self, value: Option<Cow<'a, T>>) { | |
self.value = value; | |
} | |
fn get_value(&self) -> Option<&Cow<'a, T>> { | |
self.value.as_ref() | |
} | |
} | |
/// Input control. | |
#[derive(Debug)] | |
pub struct Fieldset<'a> { | |
name: Option<Cow<'a, str>>, | |
elements: Option<Vec<FCVariant<'a>>> | |
} | |
/// Input impl. | |
impl<'a> Fieldset<'a> { | |
pub fn new(name: Option<Cow<'a, str>>, elements: Option<Vec<FCVariant<'a>>>) -> Self { | |
Fieldset { | |
name, | |
elements | |
} | |
} | |
/// Example referencing `Cow<...>` values internally. | |
pub fn print_info(&self) { | |
println!("{:?}: null; Size: {}", self.name.as_deref(), size_of_val(self)); | |
} | |
} | |
fn main() { | |
println!("Form control elements collection example\n"); | |
// Vec with multiple control types | |
let inputs = vec![ | |
UsizeInput(Input::<usize>::new( | |
Some(Cow::from("input-name-1")), | |
Some(Cow::Owned(99)) | |
)), | |
StrInput(Input::<&str>::new( | |
Some(Cow::from("input-2")), | |
Some(Cow::Borrowed(&"hello-world")) | |
)), | |
BoolInput(Input::<bool>::new( | |
Some(Cow::from("input-3")), | |
Some(Cow::Owned(false)) | |
)), | |
UsizeButton(Button::<usize>::new( | |
Some(Cow::from("button-1")), | |
Some(Cow::Owned(99)) | |
)), | |
StrButton(Button::<&str>::new( | |
Some(Cow::from("button-2")), | |
Some(Cow::Borrowed(&"hello-world")) | |
)), | |
BoolButton(Button::<bool>::new( | |
Some(Cow::from("button-3")), | |
Some(Cow::Owned(false)) | |
)), | |
UnitFieldset(Fieldset::new( | |
Some(Cow::from("fieldset-1")), | |
Some(vec![ | |
StrButton(Button::<&str>::new( | |
Some(Cow::from("button-4")), | |
Some(Cow::Borrowed(&"hello-world")) | |
)), | |
BoolButton(Button::<bool>::new( | |
Some(Cow::from("button-5")), | |
Some(Cow::Owned(false)) | |
)), | |
]) | |
)) | |
]; | |
println!("Key Value Pairs:\n"); | |
// Print input contents | |
inputs.iter().for_each(|input_enum|{ | |
input_enum.print_info(); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment