2 releases
new 0.1.1 | Nov 27, 2024 |
---|---|
0.1.0 | Nov 27, 2024 |
#1078 in Development tools
232 downloads per month
72KB
2.5K
SLoC
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
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
# #[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 = <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 singleusize
literal (e.g.2
is equivalent to2..=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 exampleVerType!(Bar<ver_gen!(1, T)>)
would expand toBarV0<>
for version 0 andBarV1<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.
Dependencies
~220–660KB
~16K SLoC