# optionable
[The optionable crate](https://crates.io/crates/optionable) is a library to derive `optioned` structs/enums versions of existing types
where all fields have been recursively replaced with versions that support setting just a subset of the relevant fields (or none at all).
One motivation for this concept is the common problem when expressing patches e.g. for [Kubernetes apply configurations](https://pkg.go.dev/k8s.io/client-go/applyconfigurations)
that for a given rust struct `T` a corresponding struct `T::Optioned` would be required where all fields are recursively optional
to specify.
While trivial to write for plain structures this quickly becomes tedious for nested structs/enums.
## Deriving optional structs/enums
The core utility of this library is to provide an `Optionable`-derive macro that derives such an optioned type.
It supports nested structures, enums as well as various container types.
The general logic is the same as for other rust derives, If you want to use the derive `Optionable` for a struct/enum
every field of it needs to also have implemented the corresponding `Optionable` trait (see below):
```rust
#[derive(Optionable)]
#[optionable(derive(Default,Serialize,Deserialize))]
struct Address {
street_name: String,
number: u8,
}
fn example(){
let _ = AddressOpt{
street_name: Some("a".to_owned()),
// fill the other fields with `None`
..Default::default()
};
}
```
The generated optioned type is (shown here with resolved associated types) as follows:
```rust
#[derive(Serialize,Deserialize)]
struct AddressOpt {
street_name: Option<String>,
number: Option<u8>,
}
```
### Enum support
Deriving optioned versions also works with enums:
```rust
#[derive(Optionable)]
enum AddressEnum {
Plain(String),
AddressExplicit { street: String, number: u32 },
AddressNested(Address)
}
fn example(){
let _ = AddressEnumOpt::AddressExplicit{
street: Some("a".to_owned()),
number: None
};
}
```
## Conversion
Per default also conversion traits for struct/enums with sized fields will be generated.
The relevant traits are (shown here without comments and `where` clauses):
```rust
pub trait OptionableConvert: Sized + Optionable {
fn into_optioned(self) -> Self::Optioned;
fn try_from_optioned(value: Self::Optioned) -> Result<Self, Error>;
fn merge(&mut self, other: Self::Optioned) -> Result<(), Error>;
}
// auto-implemented from `OptionableConvert`
pub trait OptionedConvert<T>: Sized + Sealed<T>
{
fn from_optionable(value: T) -> Self;
fn try_into_optionable(self) -> Result<T, Error>;
}
```
## How it works
The main `Optionable` trait is quite simple:
```rust
pub trait Optionable {
type Optioned;
}
```
It is a marker trait that allows to express for a given type `T` which type should be considered its `T::Optioned` type
such that `Option<Optioned>` would represent all variants of partial completeness.
For types without inner structure this means that the `Optioned` type will just resolve to the type itself, e.g.
```rust
impl Optionable for String {
type Optioned = String;
}
```
For many primitive types as well as common wrapper or collection types the `Optionable`-trait is already implemented.
## Crate features
- `chrono`: Derive `Optionable` for types from [chrono](https://docs.rs/chrono/latest/chrono/)
- `serde_json`: Derive `Optionable` for [serde_json](https://docs.rs/serde_json/latest/serde_json/)::Value
## Limitations
### External types
Due to the orphan rule the usage of the library becomes cumbersome if one has a use case which heavily relies on crate-external types.
For well-established libraries adding corresponding `impl` to this crate (feature-gated) would be a worthwhile approach.
### IDE: Resolving associated types
Due to the use of associated types some IDE-hints do not fully resolve the associated types leaving you with
`<i32 as Optionable>::Optioned` instead of `i32`. Luckily, for checking type correctness and also for error messages
when using wrong types the associated types are resolved.
## Similar crates
Another crate with similar scope is [optional_struct](https://crates.io/crates/optional_struct).
It focuses specifically on structs (not enums) and offers a more manual approach, especially in respect to nested sub-struct,
providing many fine-grained configuration options.
### License
You can use this under the conditions of the [MIT license](LICENSE-MIT) or the [Apache License, Version 2.0](LICENSE-APACHE) at your option.
#### Contributing
Any contributor has to agree to have their contribution also dual-licensed under the MIT as well as Apache-2.0 license as
specified above in the `License` subsection.