diff --git a/.gitignore b/.gitignore index 2ceade0..7c57b42 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,7 @@ target/ /target /Cargo.lock -/.idea \ No newline at end of file +/.idea +tagged-core/.idea/vcs.xml +tagged-core/.idea/tagged-core.iml +tagged-core/.idea/modules.xml diff --git a/Cargo.toml b/Cargo.toml index d5481fb..5cff318 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-tagged" -version = "0.1.0" +version = "0.4.0" edition = "2024" authors = ["Codefonsi "] license = "MPL-2.0" @@ -17,45 +17,25 @@ include = ["src/**/*", "Cargo.toml", "../../README.md", "LICENSE"] [workspace] resolver = "3" # or "3" members = [ - "tagged-chrono", "tagged-core", "tagged-macros", ] -#[lib] -#path = "crates/tagged-core/src/lib.rs" - -#[workspace.dependencies] -#tagged-core = {path = "crates/tagged-core"} -# - [patch.crates-io] tagged-core = { path = "tagged-core" } [dependencies] -tagged-core = { path = "tagged-core", version = "0.1.0", features = ["serde"] } +tagged-core = { path = "tagged-core", version = "0.4.0", features = ["serde"] } + +[dev-dependencies] serde = { version = "1.0.219", features = ["derive"] } -#serde = { version = "1.0", features = ["derive"], optional = true} -serde_json = "1.0.140" +serde_json = {version = "1.0.140"} + [workspace.dependencies] -#tagged-core = { path = "crates/tagged-core", version = "0.1.0" } -#serde = { version = "1.0", optional = true, features = ["derive"] } -#uuid = { version = "1.4", optional = true } -#chrono = { version = "0.4", optional = true } - -#[dependencies] -#tagged-core.workspace = true -#serde = { version = "1.0", features = ["derive"] } -#uuid = { version = "1.4" } -#chrono = { version = "0.4" } - -# crates/tagged-core/Cargo.toml + + [features] default = [] serde = ["tagged-core/serde"] -full = ["serde"] -#serde = ["dep:serde"] -#serde = ["dep:serde"] -#uuid = ["dep:uuid"] -#chrono = ["dep:chrono"] +full = ["serde"] \ No newline at end of file diff --git a/README.md b/README.md index b21306e..4eaa5dd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# rust-tagged +# rust-tagged (v0.4.0) > A lightweight, extensible system for creating type-safe IDs, email addresses, and domain-specific values using Rust's type system. @@ -26,7 +26,6 @@ * Lightweight `Tagged` abstraction * `From` and `Into` implementations for easy use * Optional `Deref`, `Display`, `Serialize`, and `Deserialize` support -* Custom derive macro `#[derive(Tagged)]` --- @@ -34,23 +33,107 @@ ```toml [dependencies] -rust-tagged = "0.1" +rust-tagged = "0.4.0" ``` To enable serde support: ```toml [dependencies.rust-tagged] -version = "0.1" +version = "0.4.0" features = ["serde"] ``` +## ๐Ÿงช Example - Debug + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct UserIdTag { + a: Tagged, + b: Tagged, +} + +fn main() { + let instance = UserIdTag { a: 1.into(), b: 2.into() }; + println!("{}", instance.a); + println!("{:?}", instance.b); +} +``` + +--- + +## ๐Ÿ” Example - Hash + +```rust +use tagged_core::Tagged; +use std::collections::HashSet; + +#[derive(Clone, Hash, Debug, PartialEq, Eq)] +struct User { + id: Tagged, +} + +fn main() { + let mut s: HashSet = HashSet::new(); + let user = User { id: "me@example.com".into() }; + s.insert(user.clone()); + + assert!(s.contains(&user)); +} +``` + +--- + +## ๐Ÿ” Example - Iter + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct Org; + +type EmployeeNames = Tagged, Org>; + +fn main() { + let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]); + + for name in &names { + println!("Name: {name}"); + } + + for name in names { + println!("Owned: {name}"); + } +} +``` + +--- + +## โœ๏ธ Example - Mutation + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct Org; + +type OrgName = Tagged; + +fn main() { + let mut name = OrgName::new("Codefonsi".into()); + name.set("New Org Name".into()); + + println!("Updated Org Name: {}", name.value()); +} +``` --- ## ๐Ÿ“ฆ Custom `Tagged` API ```rust -use rust_tagged::{Tagged}; +use tagged_core::Tagged; #[derive(Debug)] struct EmailTag; @@ -67,10 +150,12 @@ fn main() { } ``` +--- + ## ๐Ÿ”ฐ Getting Started (Easy) ```rust -use rust_tagged::Tagged; +use tagged_core::Tagged; struct Employee { id: Tagged, @@ -84,11 +169,11 @@ struct Org { name: String, } -fn send_mail_employee(mail_id: &Tagged, message: &str) { +fn send_mail_employee(mail_id: &Tagged, message: &str) { send_mail(mail_id, message); } -fn send_mail_org(mail_id: &Tagged, message: &str) { +fn send_mail_org(mail_id: &Tagged, message: &str) { send_mail(mail_id, message); } @@ -96,7 +181,6 @@ fn send_mail(mail_id: &str, message: &str) { println!("Mail Sent.{}", message); } - fn main() { let emp = Employee { id: 12.into(), @@ -108,48 +192,30 @@ fn main() { }, }; - // here we can clearly define and distinct the mail id of employee and org - // without - // // expected `&Tagged`, but found `&Tagged` - // send_mail_org(&emp.employee_email_id, "This is supposed to send to user but there is no type safety at compile time"); - // - // // expected `&Tagged`, but found `&Tagged` - // send_mail_employee(&emp.org.org_email_id, "This is supposed to send to user but there is no type safety at compile time"); - // - // // after refactoring - // // the trait bound `Tagged: From>` is not satisfied [E0277] - // send_mail_employee(&emp.org.org_email_id.into(), "This is ok"); - - // We don't need review and refactoring the code for runtime mistakes. send_mail_org(&emp.org.org_email_id, "This is ok"); send_mail_employee(&emp.employee_email_id, "This is ok"); - - } ``` ### โœ… Output ``` -UserId: 42 -Email: user@example.com +Mail Sent.This is ok +Mail Sent.This is ok ``` --- -## Coming Soon ## ๐Ÿงฑ Medium: Nesting in Domain Models ```rust -use rust_tagged::*; -use rust_tagged_macros::Tagged; +use tagged_core::*; use uuid::Uuid; -#[derive(Tagged)] -pub struct OrgId(Tagged); +struct Org; -#[derive(Tagged)] -pub struct OrgEmail(Tagged); +type OrgId = Tagged; +type OrgEmail = Tagged; #[derive(Debug)] struct Organization { @@ -170,20 +236,17 @@ fn main() { --- -## Coming Soon -## Timestamped Resources with `chrono` + `serde` +## ๐Ÿ”’ Hard: Timestamped Resources with `chrono` + `serde` ```rust -use rust_tagged::*; -use rust_tagged_macros::Tagged; +use tagged_core::*; use chrono::{DateTime, Utc}; use serde::{Serialize, Deserialize}; -#[derive(Tagged, Serialize, Deserialize)] -pub struct CreatedAt(Tagged, Audit>); +struct Audit; -#[derive(Tagged, Serialize, Deserialize)] -pub struct UpdatedAt(Tagged, Audit>); +type CreatedAt = Tagged, Audit>; +type UpdatedAt = Tagged, Audit>; #[derive(Serialize, Deserialize, Debug)] struct BlogPost { @@ -207,25 +270,10 @@ fn main() { --- ---- - -## Coming Soon -## ๐Ÿ”Œ Serde Integration - -When enabled via `features = ["serde"]`, tagged types auto-serialize like their inner types. - -```rust -#[derive(Tagged, Serialize, Deserialize)] -pub struct UserId(Tagged); - -let id = UserId::from(10); -let json = serde_json::to_string(&id)?; // "10" -``` - --- ## ๐Ÿ“ƒ License -Licensed under either of +Licensed under either of: * Mozilla Public License 2.0 diff --git a/examples/Iter_example.rs b/examples/Iter_example.rs index 0d201b1..f6c2f1c 100644 --- a/examples/Iter_example.rs +++ b/examples/Iter_example.rs @@ -23,6 +23,4 @@ Name: Alice Name: Bob Owned: Alice Owned: Bob - -Process finished with exit code 0 */ \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5ee3a72..2cf777b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,113 @@ -// pub mod rust_tagged{ -// pub use tagged_core::*; -// } +/// rust-tagged provides a simple way to define strongly typed wrappers over primitive types like String, i32, Uuid, chrono::DateTime, etc. It helps eliminate bugs caused by misusing raw primitives for conceptually distinct fields such as UserId, Email, ProductId, and more. +/// +/// Eliminate accidental mixups between similar types (e.g. OrgId vs UserId) +/// Enforce domain modeling in code via the type system +/// Ergonomic .into() support for primitive conversions +/// +/// # Example - Simple +/// +/// ``` +/// use tagged_core::{Tagged}; +/// +/// #[derive(Debug)] +/// struct EmailTag; +/// +/// type Email = Tagged; +/// +/// fn main() { +/// let email: Email = "test@example.com".into(); +/// println!("Email inner value: {}", email.value()); +/// +/// // Convert back to String +/// let raw: String = email.into(); +/// println!("Raw String: {raw}"); +/// } +/// ``` +/// +/// # Example - Debug +/// ``` +/// use tagged_core::Tagged; +/// +/// +/// #[derive(Debug)] +/// struct UserIdTag { +/// a: Tagged, +/// b: Tagged, +/// } +/// +/// +/// fn main() { +/// let instance = UserIdTag{a: 1.into(), b: 2.into()}; +/// +/// println!("{}", instance.a); +/// println!("{:?}", instance.b); +/// } +/// ``` +/// +/// # Example - Hash +/// ``` +/// fn main() { +/// use tagged_core::Tagged; +/// use std::collections::HashSet; +/// +/// #[derive(Clone, Hash, Debug, PartialEq, Eq)] +/// struct User { +/// id: Tagged +/// } +/// let mut s: HashSet = HashSet::new(); +/// let user = User{id: "me@example.com".into()}; +/// s.insert(user.clone()); +/// +/// assert!(s.contains(&user)); +/// } +/// ``` +/// +/// # Example - Iter +/// ``` +/// use tagged_core::Tagged; +/// +/// #[derive(Debug)] +/// struct Org; +/// +/// type EmployeeNames = Tagged, Org>; +/// +/// fn main() { +/// let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]); +/// +/// for name in &names { +/// println!("Name: {name}"); +/// } +/// +/// // Consuming iterator +/// for name in names { +/// println!("Owned: {name}"); +/// } +/// } +/// +/// /* +/// Name: Alice +/// Name: Bob +/// Owned: Alice +/// Owned: Bob +/// */ +/// ``` +/// +/// # Example - Mutation +/// ``` +/// use tagged_core::Tagged; +/// +/// #[derive(Debug)] +/// struct Org; +/// +/// type OrgName = Tagged; +/// +/// fn main() { +/// let mut name = OrgName::new("Codefonsi".into()); +/// +/// name.set("New Org Name".into()); +/// +/// println!("Updated Org Name: {}", name.value()); +/// } +/// ``` pub use tagged_core::*; diff --git a/tagged-chrono/Cargo.toml b/tagged-chrono/Cargo.toml deleted file mode 100644 index 7ab0f15..0000000 --- a/tagged-chrono/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "tagged-chrono" -version = "0.1.0" -edition = "2024" - -[dependencies] diff --git a/tagged-chrono/src/lib.rs b/tagged-chrono/src/lib.rs deleted file mode 100644 index e69de29..0000000 diff --git a/tagged-core/.idea/.gitignore b/tagged-core/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/tagged-core/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/tagged-core/.idea/modules.xml b/tagged-core/.idea/modules.xml new file mode 100644 index 0000000..50a17d6 --- /dev/null +++ b/tagged-core/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/tagged-core/.idea/tagged-core.iml b/tagged-core/.idea/tagged-core.iml new file mode 100644 index 0000000..faa1102 --- /dev/null +++ b/tagged-core/.idea/tagged-core.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tagged-core/.idea/vcs.xml b/tagged-core/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/tagged-core/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tagged-core/Cargo.toml b/tagged-core/Cargo.toml index c379798..5c2fe15 100644 --- a/tagged-core/Cargo.toml +++ b/tagged-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tagged-core" -version = "0.1.0" +version = "0.4.0" edition = "2024" description = "A lightweight tagged type abstraction for type-safe IDs, etc." license = "MPL-2.0" @@ -16,6 +16,11 @@ include = ["src/**/*", "Cargo.toml", "../../README.md", "LICENSE"] [dependencies] serde = { version = "1.0.219", features = ["derive"], optional = true } +[dev-dependencies] +uuid = { version = "1.6" , features = ["v4"]} +chrono = "0.4.41" + [features] default = [] serde = ["dep:serde"] +full = ["serde"] diff --git a/tagged-core/README.md b/tagged-core/README.md new file mode 100644 index 0000000..4eaa5dd --- /dev/null +++ b/tagged-core/README.md @@ -0,0 +1,279 @@ +# rust-tagged (v0.4.0) + +> A lightweight, extensible system for creating type-safe IDs, email addresses, and domain-specific values using Rust's type system. + +`rust-tagged` provides a simple way to define strongly typed wrappers over primitive types like `String`, `i32`, `Uuid`, `chrono::DateTime`, etc. It helps eliminate bugs caused by misusing raw primitives for conceptually distinct fields such as `UserId`, `Email`, `ProductId`, and more. + +## ๐Ÿง  Why Use Tagged Types? + +* Eliminate accidental mixups between similar types (e.g. `OrgId` vs `UserId`) +* Enforce domain modeling in code via the type system +* Ergonomic `.into()` support for primitive conversions +* Optional serde and macro support for clean `#[derive(Tagged)]` + +### ๐Ÿ“š Conceptual References + +* [Phantom types (Rust Nomicon)](https://doc.rust-lang.org/nomicon/phantom-data.html) +* [Tagged unions (Wikipedia)](https://en.wikipedia.org/wiki/Tagged_union) +* [Phantom types in Haskell](https://wiki.haskell.org/Phantom_type) +* [Newtype pattern in Rust](https://doc.rust-lang.org/rust-by-example/generics/new_types.html) +* [Algebraic data types](https://en.wikipedia.org/wiki/Algebraic_data_type) + +--- + +## โœจ Features + +* Lightweight `Tagged` abstraction +* `From` and `Into` implementations for easy use +* Optional `Deref`, `Display`, `Serialize`, and `Deserialize` support + +--- + +## ๐Ÿ›  Installation + +```toml +[dependencies] +rust-tagged = "0.4.0" +``` + +To enable serde support: + +```toml +[dependencies.rust-tagged] +version = "0.4.0" +features = ["serde"] +``` +## ๐Ÿงช Example - Debug + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct UserIdTag { + a: Tagged, + b: Tagged, +} + +fn main() { + let instance = UserIdTag { a: 1.into(), b: 2.into() }; + println!("{}", instance.a); + println!("{:?}", instance.b); +} +``` + +--- + +## ๐Ÿ” Example - Hash + +```rust +use tagged_core::Tagged; +use std::collections::HashSet; + +#[derive(Clone, Hash, Debug, PartialEq, Eq)] +struct User { + id: Tagged, +} + +fn main() { + let mut s: HashSet = HashSet::new(); + let user = User { id: "me@example.com".into() }; + s.insert(user.clone()); + + assert!(s.contains(&user)); +} +``` + +--- + +## ๐Ÿ” Example - Iter + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct Org; + +type EmployeeNames = Tagged, Org>; + +fn main() { + let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]); + + for name in &names { + println!("Name: {name}"); + } + + for name in names { + println!("Owned: {name}"); + } +} +``` + +--- + +## โœ๏ธ Example - Mutation + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct Org; + +type OrgName = Tagged; + +fn main() { + let mut name = OrgName::new("Codefonsi".into()); + name.set("New Org Name".into()); + + println!("Updated Org Name: {}", name.value()); +} +``` + +--- + +## ๐Ÿ“ฆ Custom `Tagged` API + +```rust +use tagged_core::Tagged; + +#[derive(Debug)] +struct EmailTag; + +type Email = Tagged; + +fn main() { + let email: Email = "test@example.com".into(); + println!("Email inner value: {}", email.value()); + + // Convert back to String + let raw: String = email.into(); + println!("Raw String: {raw}"); +} +``` + +--- + +## ๐Ÿ”ฐ Getting Started (Easy) + +```rust +use tagged_core::Tagged; + +struct Employee { + id: Tagged, + employee_email_id: Tagged, + name: String, + org: Org, +} + +struct Org { + org_email_id: Tagged, + name: String, +} + +fn send_mail_employee(mail_id: &Tagged, message: &str) { + send_mail(mail_id, message); +} + +fn send_mail_org(mail_id: &Tagged, message: &str) { + send_mail(mail_id, message); +} + +fn send_mail(mail_id: &str, message: &str) { + println!("Mail Sent.{}", message); +} + +fn main() { + let emp = Employee { + id: 12.into(), + employee_email_id: "akash@gmail.com".into(), + name: "Akash".into(), + org: Org { + org_email_id: "info@codefonsi.com".into(), + name: "Codefonsi".into(), + }, + }; + + send_mail_org(&emp.org.org_email_id, "This is ok"); + send_mail_employee(&emp.employee_email_id, "This is ok"); +} +``` + +### โœ… Output + +``` +Mail Sent.This is ok +Mail Sent.This is ok +``` + +--- + +## ๐Ÿงฑ Medium: Nesting in Domain Models + +```rust +use tagged_core::*; +use uuid::Uuid; + +struct Org; + +type OrgId = Tagged; +type OrgEmail = Tagged; + +#[derive(Debug)] +struct Organization { + id: OrgId, + email: OrgEmail, +} + +fn main() { + let org = Organization { + id: Uuid::new_v4().into(), + email: "contact@company.com".into(), + }; + + println!("Org ID: {}", org.id); + println!("Org Email: {}", org.email); +} +``` + +--- + +## ๐Ÿ”’ Hard: Timestamped Resources with `chrono` + `serde` + +```rust +use tagged_core::*; +use chrono::{DateTime, Utc}; +use serde::{Serialize, Deserialize}; + +struct Audit; + +type CreatedAt = Tagged, Audit>; +type UpdatedAt = Tagged, Audit>; + +#[derive(Serialize, Deserialize, Debug)] +struct BlogPost { + title: String, + created: CreatedAt, + updated: UpdatedAt, +} + +fn main() { + let post = BlogPost { + title: "Type-Safe Rust APIs".into(), + created: Utc::now().into(), + updated: Utc::now().into(), + }; + + let json = serde_json::to_string_pretty(&post).unwrap(); + println!("Serialized: \n{json}"); +} +``` + +--- + + +--- + +## ๐Ÿ“ƒ License + +Licensed under either of: + +* Mozilla Public License 2.0 diff --git a/tagged-core/examples/uuid_and_chrono.rs b/tagged-core/examples/uuid_and_chrono.rs new file mode 100644 index 0000000..0cc8907 --- /dev/null +++ b/tagged-core/examples/uuid_and_chrono.rs @@ -0,0 +1,31 @@ +use chrono::{DateTime, Utc}; +use uuid::{Uuid}; +use tagged_core::Tagged; + +#[derive(Debug)] +struct SomeStruct { + id: Tagged, + time_id: Tagged, Self>, +} + +impl SomeStruct { + fn new(id: Uuid, email: DateTime) -> Self { + Self { + id: id.into(), + time_id: email.into(), + } + } +} + + + +fn main() { + let id = Uuid::new_v4(); + let instance = SomeStruct::new(id , Utc::now()); + println!("{:?}", instance); + +} + +/* +SomeStruct { id: 15e74217-09a3-453f-b9f1-c47c3df84c34, time_id: 2025-07-02T03:08:13.726417Z } +*/ \ No newline at end of file diff --git a/tagged-core/src/lib.rs b/tagged-core/src/lib.rs index 9739abb..5f5e331 100644 --- a/tagged-core/src/lib.rs +++ b/tagged-core/src/lib.rs @@ -8,26 +8,112 @@ use std::hash::{Hash, Hasher}; /// Eliminate accidental mixups between similar types (e.g. OrgId vs UserId) /// Enforce domain modeling in code via the type system /// Ergonomic .into() support for primitive conversions -/// -/// # Example -/// +/// +/// # Example - Simple +/// /// ``` /// use tagged_core::{Tagged}; -/// +/// /// #[derive(Debug)] /// struct EmailTag; -/// +/// /// type Email = Tagged; -/// +/// /// fn main() { /// let email: Email = "test@example.com".into(); /// println!("Email inner value: {}", email.value()); -/// +/// /// // Convert back to String /// let raw: String = email.into(); /// println!("Raw String: {raw}"); /// } /// ``` +/// +/// # Example - Debug +/// ``` +/// use tagged_core::Tagged; +/// +/// +/// #[derive(Debug)] +/// struct UserIdTag { +/// a: Tagged, +/// b: Tagged, +/// } +/// +/// +/// fn main() { +/// let instance = UserIdTag{a: 1.into(), b: 2.into()}; +/// +/// println!("{}", instance.a); +/// println!("{:?}", instance.b); +/// } +/// ``` +/// +/// # Example - Hash +/// ``` +/// fn main() { +/// use tagged_core::Tagged; +/// use std::collections::HashSet; +/// +/// #[derive(Clone, Hash, Debug, PartialEq, Eq)] +/// struct User { +/// id: Tagged +/// } +/// let mut s: HashSet = HashSet::new(); +/// let user = User{id: "me@example.com".into()}; +/// s.insert(user.clone()); +/// +/// assert!(s.contains(&user)); +/// } +/// ``` +/// +/// # Example - Iter +/// ``` +/// use tagged_core::Tagged; +/// +/// #[derive(Debug)] +/// struct Org; +/// +/// type EmployeeNames = Tagged, Org>; +/// +/// fn main() { +/// let names: EmployeeNames = Tagged::new(vec!["Alice".into(), "Bob".into()]); +/// +/// for name in &names { +/// println!("Name: {name}"); +/// } +/// +/// // Consuming iterator +/// for name in names { +/// println!("Owned: {name}"); +/// } +/// } +/// +/// /* +/// Name: Alice +/// Name: Bob +/// Owned: Alice +/// Owned: Bob +/// */ +/// ``` +/// +/// # Example - Mutation +/// ``` +/// use tagged_core::Tagged; +/// +/// #[derive(Debug)] +/// struct Org; +/// +/// type OrgName = Tagged; +/// +/// fn main() { +/// let mut name = OrgName::new("Codefonsi".into()); +/// +/// name.set("New Org Name".into()); +/// +/// println!("Updated Org Name: {}", name.value()); +/// } +/// ``` pub struct Tagged { value: T, _marker: std::marker::PhantomData, @@ -171,6 +257,7 @@ impl Tagged { } } + // For all common primitive types // macro_rules! impl_from_tagged { // ($($t:ty),*) => {