odin-union 0.1.0

Dependency-free shorthand for Rust enums whose variants wrap same-named types
Documentation
// Copyright (C) 2026  Sisyphus1813
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use odin_union::odin_union;

#[derive(Debug, PartialEq)]
struct Person {
    name: &'static str,
}

#[derive(Debug, PartialEq)]
struct Animal {
    species: &'static str,
}

#[odin_union]
#[derive(Debug, PartialEq)]
enum Thing {
    /// A person payload.
    Person,
    Animal,
}

#[odin_union]
enum ConditionalThing {
    #[cfg(any())]
    MissingType,
    Animal,
}

#[test]
fn constructs_variants_directly() {
    let thing = Thing::Person(Person { name: "Ada" });

    match thing {
        Thing::Person(person) => assert_eq!(person.name, "Ada"),
        Thing::Animal(animal) => panic!("unexpected animal: {animal:?}"),
    }
}

#[test]
fn converts_each_payload_with_from() {
    let person: Thing = Person { name: "Ada" }.into();
    let animal = Thing::from(Animal { species: "cat" });

    assert_eq!(person, Thing::Person(Person { name: "Ada" }));
    assert_eq!(animal, Thing::Animal(Animal { species: "cat" }),);
}

#[test]
fn matches_like_an_ordinary_payload_enum() {
    let thing: Thing = Animal { species: "fox" }.into();

    match thing {
        Thing::Person(person) => panic!("unexpected person: {person:?}"),
        Thing::Animal(animal) => assert_eq!(animal.species, "fox"),
    }
}

#[test]
fn copies_conditional_attributes_to_generated_implementations() {
    let thing: ConditionalThing = Animal { species: "owl" }.into();

    match thing {
        ConditionalThing::Animal(animal) => assert_eq!(animal.species, "owl"),
    }
}