# Versioned types for Rust This is a procedural macro that makes writing types that have multiple versions short and easy! This is done by just putting `#[versioned]` on a single type definition, which will then be converted into multiple types (one for each version) and, optionally, an enum of all versions of the type. The versions have to be `usize`s. ## Example ```rust use verty::versioned; # #[versioned(start = 1, end = 3)] struct Schema { /* ... */} #[versioned(start = 1)] struct CustomerData { name: String, #[ver = 1..=2] age: u8, #[ver = 3..] is_over_18: bool, last_login_ip: ver_match! { 1 => { std::net::Ipv4Addr }; 2.. => { std::net::IpAddr } }, #[ver = 2] agreed_to_cookies: bool, schema: VerType!(Schema), } ``` expands to ```rust # #[verty::versioned(start = 1, end = 3)] struct Schema { /* ... */} struct CustomerDataV1 { name: String, age: u8, last_login_ip: std::net::Ipv4Addr, schema: SchemaV1 } struct CustomerDataV2 { name: String, age: u8, last_login_ip: std::net::IpAddr, agreed_to_cookies: bool, schema: SchemaV2 } struct CustomerDataV3 { name: String, is_over_18: bool, last_login_ip: std::net::IpAddr, schema: SchemaV3 } ``` ## Features All three kinds of types are supported: `struct`, `enum`, `union`. You can put a `#[ver = ]` helper attribute on any fields, variants, variant fields, or generic parameters to restrict which versions of the type they appear in. - The number of versions the type has will be determined by the highest version explicitly mentioned in any of these helper attributes. - The range must be a range literal of `usize` literals or a single `usize` literal (e.g. `2` is equivalent to `2..=2`) - The version types are called `V` by default, where `` is the name of the input type and `` is the version. You can put a `#[ver_attr(, )]` helper attribute anywhere to apply `#[]` in its place, but only for versions in ``. This works exactly like `#[cfg_attr(...)]`. You can put a `#[ver_where(, )]` helper attribute _on the type definition_ to apply `where ` to it, but only for versions in ``. You can use the `ver_match!` pseudo-macro to produce a statement/expression/type/item depending on the expanded version. The syntax is `ver_match! { $( => { });+ $(;)? }`. Note that each version must appear exactly once, no more, no less. You can use the `VerType!` pesudo-macro as a field type to get version-dependent types. For example, `VerType!(Foo)` expands to `FooV` in version ``. - The input of `VerType!` can also contain pseudo-macros, which are expanded accordingly, for example `VerType!(Bar)` would expand to `BarV0<>` for version 0 and `BarV1` for version 1. You can use the `ver_gen!` pseudo-macro in generic argument lists to only include a generic argument for a given version. Its syntax is `ver_gen!(, )`. You can give arguments to the macro (`#[versioned()]` instead of `#[versioned]`) to influence code generation. The options are comma-separated, with the following possibilities: - `start = `: Let `` be the first version instead of 0. This is probably mostly useful with 1 (i.e. `start = 1`). Also makes `..` ranges start with `` instead of 0. Also makes using a version number below it anywhere an error. - `end = `: Let `` be the last version instead of the highest mentioned one. Also makes using a version number above it anywhere an error. - `rename( => )`: Rename the type for version `` to ``. ## Similar crates - `duplicate` is very useful to copy-paste items with slight changes between each of them, but it requires a moderate amount of boilerplate, especially when you have more complex differences between the different copies.