Expand description
§Advanced Architecture (ADAR)
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
serdefeature) - 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
- State callback:
- Machine callback:
- on_update - Called when update is called
- on_transition - Called at each transition (after on_leave, before on_enter)
- Pass arguments to updates (see update_args, run_args, transition_args)
- Store context in the StateMachine (see new_context, with up to 8 generic parameters)
- Operating modes
- Non-blocking mode (see update_args)
- Blocking mode (see run_args)
- End state (see EndState, is_finished)
- Sync only
§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
- Reflects the underlying type (see ReflectEnum::Type)
- You may define your own repr (e.g.
#[repr(u8)])
- You may define your own repr (e.g.
- Reflects the name and value, or iterates over enum variants (see ReflectEnum::variants,EnumVariant)
- Number of variants (see ReflectEnum::count)
- Name of the enum (see ReflectEnum::name)
§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, NoneClick 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
- Deref implementation (see EnumTraitDeref)
- DerefMut implementation (see EnumTraitDerefMut, also implements EnumTraitDeref trait)
§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 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
- Concatenation (When the exact type is known at compile time, see TupleConcat::concat())
- Iteration (Only for homogeneous tuples, see TupleIter::iter())
- Iteration over implemented trait (Each tuple element needs to implement the trait, see TupleTraitIter::iter_trait(), TupleTraitIterMut::iter_trait_mut())
- Select tuple element by type (The tuple must contain exactly one element of the given type, see TupleSelect::select)
§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: trueClick 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;