okstd
Standards that are OK.
Motivation
Rust's ecosystem is known for its vibrant community and wealth of high-quality crates. However, this abundance has led to some fragmentation, especially when it comes to foundational aspects like asynchronous programming, I/O, and logging. Developers are often faced with choosing between multiple ways of handling async code (e.g., async-std, tokio, smol, surf), several approaches to async I/O, and numerous logging frameworks (e.g., log, env_logger, pretty_env_logger, femme, flexi_logger). While having options is valuable, it can also lead to decision paralysis and make it harder to build an ecosystem of interoperable libraries and frameworks.
This is the crate when we reach for when we need such primitives, essentially the "Battery Included" crate.
Getting Started
cargo add okstd@0.1.0
#![allow(unused)] fn main() { use okstd::prelude::*; }
Examples
okstd::main
#[cfg(feature = "macros")] { use okstd::prelude::*; fn something() { println!("Hello, world!"); } #[okstd::main] async fn main() { something(); } }
macros
okstd::log
A proc macro for verifying trait implementations on enum variants.
This macro verifies at compile time that all fields in an enum's variants implement the specified traits.
Examples
Basic usage with a single trait:
#![allow(unused)] fn main() { use okstd::prelude::*; pub trait Convertible { fn convert(&self) -> String; } struct Number(i32); impl Convertible for Number { fn convert(&self) -> String { self.0.to_string() } } #[impls(Convertible)] enum Data { Num(Number), // OK - Number implements Convertible } }
Multiple trait bounds:
#![allow(unused)] fn main() { use okstd::prelude::*; use std::fmt::Debug; trait Storage { fn store(&self); } #[derive(Debug)] struct File; impl Storage for File { fn store(&self) { } } #[impls(Storage, Debug)] enum Resource { FileResource(File), // OK - File implements both Storage and Debug } }
Will fail to compile if traits aren't implemented:
use okstd::prelude::*;
trait Required {}
struct Missing; // Doesn't implement Required
#[impls(Required)]
enum WontCompile {
Bad(Missing), // Error: Missing doesn't implement Required
}
Works with fully qualified trait paths:
#![allow(unused)] fn main() { use okstd::prelude::*; mod features { pub trait Advanced {} pub struct Handler; impl Advanced for Handler {} } #[impls(features::Advanced)] enum System { Complex(features::Handler), } }
Multiple variants are checked:
#![allow(unused)] fn main() { use okstd::prelude::*; trait Shared {} struct First; impl Shared for First {} struct Second; impl Shared for Second {} #[impls(Shared)] enum Multi { One(First), Two(Second), } }
Notes
- Currently only supports tuple variants
- All fields in a variant must implement all specified traits
- Compile errors point to the specific variant that fails the trait bounds
okstd::main
#[cfg(feature = "macros")] { use okstd::prelude::*; fn something() { println!("Hello, world!"); } #[okstd::main] async fn main() { something(); } }
okstd::test
take a function and if it's not async, just add the #[test] attribute if it's async, add the #[test] attribute and setup the runtime take the previous function body then pass it into block_on as a closure
#![allow(unused)] fn main() { use ok_macros as okstd; #[okstd::test] fn does_something() { // do something } }
to
#![allow(unused)] fn main() { #[test] fn does_something() { // do something } }
or
#![allow(unused)] fn main() { use ok_macros as okstd; #[okstd::test] async fn does_something() { // do something } }
to
#![allow(unused)] fn main() { #[test] fn does_something() { Runtimes::setup_runtimes().unwrap().block_on(async { // do something }); } }
okstd::log
#![allow(unused)] fn main() { #[cfg(feature = "macros")] { use okstd::prelude::*; #[okstd::log(debug)] fn something() { debug!("Hello, world!"); println!("Hello, world!"); } } }