Skip to content

Instantly share code, notes, and snippets.

@elycruz
Last active April 25, 2023 21:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elycruz/d5d7bd801cefd5c6c7094aab9bc0763c to your computer and use it in GitHub Desktop.
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.
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