Skip to main content

Crate adar

Crate adar 

Source
Expand description

§Advanced Architecture (ADAR)

Crates.io Downloads Docs

Adar is a collection of architectural tools that help you write more readable and performant code.

§Table of contents

§Flags

Flags is a type-safe and verbose bitwise flag container. Most of the flag operations are inlined.

§Features

  • Union, Intersect
  • Serialization (requires serde feature)
  • Conversion to and from raw values
  • Intuitive syntax

§Example

use adar::prelude::*;

#[FlagEnum]
enum MyFlag {F1, F2, F3}

let mut a = Flags::from(MyFlag::F1);
a.set(MyFlag::F2);
let mut b = MyFlag::F1 | MyFlag::F2 | MyFlag::F3;
b.reset(MyFlag::F1);

println!("a: {:?}, {:03b}", a, a.into_raw());
println!("b: {:?}, {:03b}", b, b.into_raw());

println!("a.any(F1,F3): {:?}", a.any(MyFlag::F1 | MyFlag::F3));
println!("a.all(F1,F3): {:?}", a.all(MyFlag::F1 | MyFlag::F3));

println!("a.intersect(b): {:?}", a.intersect(b));
println!("a.union(b): {:?}", a.union(b));
println!("full(): {:?}", Flags::<MyFlag>::full());
Click to see the output >> cargo run --example flags
a: (F1,F2), 011
b: (F2,F3), 110
a.any(F1,F3): true
a.all(F1,F3): false
a.intersect(b): (F2)
a.union(b): (F1,F2,F3)
full(): (F1,F2,F3)
Click to see the generated code >> cargo expand --example flags
...
#[repr(u32)]
enum MyFlag {
    F1 = 1,
    F2 = 2,
    F3 = 4,
}
...
impl std::ops::BitOr for MyFlag
where
    Self: adar::prelude::ReflectEnum,
{
    type Output = adar::prelude::Flags<Self>;
    fn bitor(self, rhs: Self) -> Self::Output {
        Flags::empty() | self | rhs
    }
}
...

§State Machine

§Features

§Example

use adar::prelude::*;
use std::{process::Command, time::Duration};

#[StateEnum]
#[ReflectEnum] // Optional. (Used here to print the name of the state)
enum TrafficLight {
    Go,
    GetReady,
    StopIfSafe,
    Stop,
}

impl TrafficLight {
    const YELLOW_DURATION: Duration = Duration::from_secs(1);
    const GO_STOP_DURATION: Duration = Duration::from_secs(2);
}

impl Machine for TrafficLight {
    fn on_transition(&mut self, new_state: &Self::States, _context: &mut Self::Context) {
        Command::new("clear")
            .status()
            .expect("Failed to clear screen!");

        println!("{}", new_state.name());
    }
}

impl State for Go {
    fn on_enter(&mut self, _args: Option<&mut Self::Args>, _context: &mut Self::Context) {
        println!("⚫\n⚫\n🟢");
    }
    fn on_update(
        &mut self,
        _args: Option<&mut Self::Args>,
        _context: &mut Self::Context,
    ) -> Option<Self::States> {
        std::thread::sleep(TrafficLight::GO_STOP_DURATION);
        Some(StopIfSafe.into())
    }
}

impl State for GetReady {
    fn on_enter(&mut self, _args: Option<&mut Self::Args>, _context: &mut Self::Context) {
        println!("🔴\n🟡\n⚫");
    }
    fn on_update(
        &mut self,
        _args: Option<&mut Self::Args>,
        _context: &mut Self::Context,
    ) -> Option<Self::States> {
        std::thread::sleep(TrafficLight::YELLOW_DURATION);
        Some(Go.into())
    }
}

impl State for StopIfSafe {
    fn on_enter(&mut self, _args: Option<&mut Self::Args>, _context: &mut Self::Context) {
        println!("⚫\n🟡\n⚫");
    }
    fn on_update(
        &mut self,
        _args: Option<&mut Self::Args>,
        _context: &mut Self::Context,
    ) -> Option<Self::States> {
        std::thread::sleep(TrafficLight::YELLOW_DURATION);
        Some(Stop.into())
    }
}

impl State for Stop {
    fn on_enter(&mut self, _args: Option<&mut Self::Args>, _context: &mut Self::Context) {
        println!("🔴\n⚫\n⚫")
    }
    fn on_update(
        &mut self,
        _args: Option<&mut Self::Args>,
        _context: &mut Self::Context,
    ) -> Option<Self::States> {
        std::thread::sleep(TrafficLight::GO_STOP_DURATION);
        Some(GetReady.into())
    }
}

fn main() {
    StateMachine::new(Stop).run();
}
Click to see the output >> cargo run --example statemachine_trafficlight
Stop
🔴
⚫
⚫
... (5s)
GetReady
🔴
🟡
⚫
... (2s)
Go
⚫
⚫
🟢
... (5s)
StopIfSafe
⚫
🟡
⚫
... (2s, then repeats)
Click to see the generated code >> cargo expand --example statemachine_trafficlight
...
enum TrafficLight {
    Go(Go),
    GetReady(GetReady),
    StopIfSafe(StopIfSafe),
    Stop(Stop),
}
impl adar::prelude::ReflectEnum for TrafficLight {
    type Type = u32;
    fn variants() -> &'static [adar::prelude::EnumVariant<TrafficLight>] {
        const VARIANTS: &[adar::prelude::EnumVariant<TrafficLight>] = &[
            EnumVariant::new("Go", None),
            EnumVariant::new("GetReady", None),
            EnumVariant::new("StopIfSafe", None),
            EnumVariant::new("Stop", None),
        ];
        VARIANTS
    }
    fn count() -> usize {
        4usize
    }
    fn name(&self) -> &'static str {
        match self {
            Self::Go { .. } => "Go",
            Self::GetReady { .. } => "GetReady",
            Self::StopIfSafe { .. } => "StopIfSafe",
            Self::Stop { .. } => "Stop",
        }
    }
}
struct Go;
impl adar::prelude::StateTypes for Go {
    type States = TrafficLight;
    type Args = ();
    type Context = ();
}
impl Into<TrafficLight> for Go {
    fn into(self) -> TrafficLight {
        TrafficLight::Go(self)
    }
}
struct GetReady;
impl adar::prelude::StateTypes for GetReady {
    type States = TrafficLight;
    type Args = ();
    type Context = ();
}
impl Into<TrafficLight> for GetReady {
    fn into(self) -> TrafficLight {
        TrafficLight::GetReady(self)
    }
}
struct StopIfSafe;
impl adar::prelude::StateTypes for StopIfSafe {
    type States = TrafficLight;
    type Args = ();
    type Context = ();
}
impl Into<TrafficLight> for StopIfSafe {
    fn into(self) -> TrafficLight {
        TrafficLight::StopIfSafe(self)
    }
}
struct Stop;
impl adar::prelude::StateTypes for Stop {
    type States = TrafficLight;
    type Args = ();
    type Context = ();
}
impl Into<TrafficLight> for Stop {
    fn into(self) -> TrafficLight {
        TrafficLight::Stop(self)
    }
}
impl adar::prelude::StateTypes for TrafficLight {
    type States = Self;
    type Args = ();
    type Context = ();
}
impl adar::prelude::State for TrafficLight {
    fn on_enter(&mut self, args: Option<&mut Self::Args>, context: &mut Self::Context) {
        match self {
            Self::Go(s) => Go::on_enter(s, args, context),
            Self::GetReady(s) => GetReady::on_enter(s, args, context),
            Self::StopIfSafe(s) => StopIfSafe::on_enter(s, args, context),
            Self::Stop(s) => Stop::on_enter(s, args, context),
            _ => {}
        }
    }
    fn on_update(
        &mut self,
        args: Option<&mut Self::Args>,
        context: &mut Self::Context,
    ) -> Option<Self::States> {
        match self {
            Self::Go(s) => Go::on_update(s, args, context),
            Self::GetReady(s) => GetReady::on_update(s, args, context),
            Self::StopIfSafe(s) => StopIfSafe::on_update(s, args, context),
            Self::Stop(s) => Stop::on_update(s, args, context),
            _ => None,
        }
    }
    fn on_leave(&mut self, args: Option<&mut Self::Args>, context: &mut Self::Context) {
        match self {
            Self::Go(s) => Go::on_leave(s, args, context),
            Self::GetReady(s) => GetReady::on_leave(s, args, context),
            Self::StopIfSafe(s) => StopIfSafe::on_leave(s, args, context),
            Self::Stop(s) => Stop::on_leave(s, args, context),
            _ => {}
        }
    }
}
...

§Reflect Enum

Reflects information about the enum and its variants.

§Features

§Example

use adar::prelude::*;

#[ReflectEnum]
#[repr(u32)]
#[derive(Debug)]
enum MyEnum {
    Value1 = 33,
    Value2(i32),
    Value3 { a: String },
}

fn main() {
    println!("Variants count: {}", MyEnum::count());
    for variant in MyEnum::variants() {
        println!("{}, {:?}", variant.name, variant.value,);
    }
}
Click to see the output >> cargo run --example reflect_enum
Variants count: 3
Value1, Some(Value1)
Value2, None
Value3, None
Click to see the generated code >> cargo expand --example reflect_enum
...
impl adar::prelude::ReflectEnum for MyEnum {
    type Type = u32;
    fn variants() -> &'static [adar::prelude::EnumVariant<MyEnum>] {
        const VARIANTS: &[adar::prelude::EnumVariant<MyEnum>] = &[
            EnumVariant::new("Value1", Some(MyEnum::Value1)),
            EnumVariant::new("Value2", None),
            EnumVariant::new("Value3", None),
        ];
        VARIANTS
    }
    fn count() -> usize {
        3usize
    }
    fn name(&self) -> &'static str {
        match self {
            Self::Value1 { .. } => "Value1",
            Self::Value2 { .. } => "Value2",
            Self::Value3 { .. } => "Value3",
        }
    }
}
...

§Enum Trait Deref

Enables you to access a trait through an enum whose named variants implement the same trait.

§Features

§Example

use adar::prelude::*;

trait MyTrait {
    fn my_func(&self);
}

#[EnumTraitDeref(MyTrait)]
enum MyEnum {
    A(A),
    B(B),
}

#[derive(Clone)]
struct A;

#[derive(Clone)]
struct B;

impl MyTrait for A {
    fn my_func(&self) {
        println!("Hello A");
    }
}

impl MyTrait for B {
    fn my_func(&self) {
        println!("Hello B");
    }
}

fn main() {
    for e in [MyEnum::A(A), MyEnum::B(B)] {
        e.my_func();
    }
}
Click to see the output >> cargo run --example enum_trait_deref
Hello A
Hello B
Click to see the generated code >> cargo expand --example enum_trait_deref
...
impl ::core::ops::Deref for MyEnum {
    type Target = dyn MyTrait;
    fn deref(&self) -> &Self::Target {
        match self {
            Self::A(v) => v as &Self::Target,
            Self::B(v) => v as &Self::Target,
        }
    }
}
...

§Tuple operations

§Features

§Example

use adar::prelude::*;

#[TraitRef]
trait ToStringCapital {
    fn to_string_capital(&self) -> String;
}

impl<T> ToStringCapital for T
where
    T: ToString,
{
    fn to_string_capital(&self) -> String {
        self.to_string()
            .chars()
            .map(|c| c.to_uppercase().next().unwrap())
            .collect()
    }
}

fn main() {
    println!("Homogeneous:");
    let homogeneous = (1, 2, 3, 4);
    for i in homogeneous.iter() {
        println!("\t{i}");
    }

    let mixed = ("String", 24, true, 2.2);
    println!("Mixed -> ToString:");
    // Automatically implemented for all std/core traits
    for i in mixed.iter_trait::<dyn ToString>() {
        println!("\t{}", i.to_string());
    }
    // Needs #[TraitRef] for custom traits
    println!("Mixed -> ToStringCapital:");
    for i in mixed.iter_trait::<dyn ToStringCapital>() {
        println!("\t{}", i.to_string_capital());
    }

    println!("Concat: {:?}", homogeneous.concat(mixed));
    println!("Sum of homogeneous: {:?}", homogeneous.iter().sum::<i32>());
    println!("F32 from mixed: {:?}", mixed.select::<f32>());
    println!("bool from mixed: {:?}", mixed.select::<bool>());
}
Click to see the output >> cargo run --example tuple_operations
Homogeneous:
        1
        2
        3
        4
Mixed -> ToString:
        String
        24
        true
        2.2
Mixed -> ToStringCapital:
        STRING
        24
        TRUE
        2.2
Concat: (1, 2, 3, 4, "String", 24, true, 2.2)
Sum of homogeneous: 10
F32 from mixed: 2.2
bool from mixed: true
Click to see the generated code >> cargo expand --example tuple_operations
...
impl<'a, T> adar::prelude::AsTraitRef<T> for dyn CustomTrait
where
    T: CustomTrait + 'static,
{
    fn as_trait_ref(value: &T) -> &Self {
        value
    }
}
impl<'a, T> adar::prelude::AsTraitMut<T> for dyn CustomTrait
where
    T: CustomTrait + 'static,
{
    fn as_trait_mut(value: &mut T) -> &mut Self {
        value
    }
}
...

Re-exports§

pub use adar_macros as macros;

Modules§

enums
prelude
state_machine
tuples