# 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)]
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
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 = <range>]` 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 `<input>V<n>` by default,
where `<input>` is the name of the input type and `<n>` is the version.
You can put a `#[ver_attr(<range>, <attr>)]` helper attribute anywhere
to apply `#[<attr>]` in its place, but only for versions in `<range>`.
This works exactly like `#[cfg_attr(...)]`.
You can put a `#[ver_where(<range>, <clauses>)]` helper attribute _on the type definition_
to apply `where <clauses>` to it, but only for versions in `<range>`.
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! { $(<range> => { <anything> });+ $(;)? }`.
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<n>` in version `<n>`.
- The input of `VerType!` can also contain pseudo-macros, which are expanded accordingly,
for example `VerType!(Bar<ver_gen!(1, T)>)` would expand to `BarV0<>` for version 0 and `BarV1<T>` 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!(<range>, <generic argument>)`.
You can give arguments to the macro (`#[versioned(<args>)]` instead of `#[versioned]`) to influence code generation.
The options are comma-separated, with the following possibilities:
- `start = <n>`: Let `<n>` be the first version instead of 0. This is probably mostly useful with 1 (i.e. `start = 1`).
Also makes `..<m>` ranges start with `<n>` instead of 0.
Also makes using a version number below it anywhere an error.
- `end = <n>`: Let `<n>` be the last version instead of the highest mentioned one.
Also makes using a version number above it anywhere an error.
- `rename(<n> => <ident>)`: Rename the type for version `<n>` to `<ident>`.
## 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.