Skip to content

Commit

Permalink
Add custom wrapper documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
roignpar committed Apr 5, 2024
1 parent e2e4362 commit 06cb9e6
Showing 1 changed file with 240 additions and 0 deletions.
240 changes: 240 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
//! * [Field attributes](#field-attributes)
//! * [Merging](#merging)
//! * [From](#from)
//! * [Custom wrappers](#custom-wrappers)
//! * [rewrap](#wrapper--rewrap)
//! * [merging](#wrapper--merge_fn)
//! * [from](#wrapper--from)
//! * [nesting](#wrapper-nesting)
//!
//! # Simple examples
//! The first argument is the name of the generated struct:
Expand Down Expand Up @@ -447,6 +452,241 @@
//! assert_eq!(from.text.unwrap(), "super");
//! assert_eq!(from.number.unwrap(), 2);
//! ```
//!
//! # Custom wrappers
//! Custom wrappers can be used instead of `Option` with the `wrapper` argument:
//! ```
//! # use optfield::*;
//! enum MyWrapper<T> {
//! Empty,
//! Number(i32),
//! Something(T),
//! }
//!
//! #[optfield(Opt, wrapper = MyWrapper)]
//! struct MyStruct {
//! optional: Option<i32>,
//! text: String,
//! }
//! ```
//! Will generate:
//! ```
//! # struct MyWrapper<T>(T);
//! struct Opt {
//! optional: MyWrapper<Option<i32>>,
//! text: MyWrapper<String>,
//! }
//! ```
//! Any type with a signature taking exactly one generic (`Wrapper<T>`) can be
//! used as a wrapper: structs, tuples, enums or type aliases.
//!
//! When using `wrapper` some behavior changes:
//!
//! ### `wrapper` + `rewrap`
//! `rewrap` will target the wrapper instead of `Option`:
//! ```
//! # use optfield::*;
//! # struct MyWrapper<T>(T);
//! #[optfield(Opt, wrapper = MyWrapper)]
//! struct MyStruct {
//! optional: Option<i32>,
//! text: String,
//! wrapped: MyWrapper<i32>,
//! }
//! ```
//! Will generate:
//! ```
//! # struct MyWrapper<T>(T);
//! struct Opt {
//! optional: MyWrapper<Option<i32>>,
//! text: MyWrapper<String>,
//! wrapped: MyWrapper<String>,
//! }
//! ```
//! Whereas
//! ```
//! # use optfield::*;
//! # struct MyWrapper<T>(T);
//! #[optfield(Opt, wrapper = MyWrapper, rewrap)]
//! struct MyStruct {
//! optional: Option<i32>,
//! text: String,
//! wrapped: MyWrapper<i32>,
//! }
//! ```
//! Will generate:
//! ```
//! # struct MyWrapper<T>(T);
//! struct Opt {
//! optional: MyWrapper<Option<i32>>,
//! text: MyWrapper<String>,
//! wrapped: MyWrapper<MyWrapper<String>>,
//! }
//! ```
//! ### `wrapper` + `merge_fn`
//! Unlike with `Option` which is part of Rust core, optfield
//! can't make any assumptions about the custom types used as wrappers, so
//! when using `merge_fn` a few extra things are required.
//!
//! A `From<MyWrapper<T>> for Option<T>` implementation must exist, either
//! generically or specifically for each type used for the original struct's
//! fields. For each field the value will be merged when
//! `Option::<T>::from(opt.field)` is `Some(...)` and ignored when it is `None`,
//! `T` being the field's type in the original struct and `opt` an instance of
//! the struct generated by optfield. Example:
//! ```
//! # use optfield::*;
//! enum MyWrapper<T> {
//! Nothing,
//! NothingElse,
//! Something(T),
//! }
//!
//! impl<T> From<MyWrapper<T>> for Option<T> {
//! fn from(value: MyWrapper<T>) -> Option<T> {
//! match value {
//! MyWrapper::Nothing => None,
//! MyWrapper::NothingElse => None,
//! MyWrapper::Something(t) => Some(t),
//! }
//! }
//! }
//!
//! #[optfield(Opt, wrapper = MyWrapper, merge_fn)]
//! struct MyStruct {
//! number: i32,
//! another_number: u32,
//! text: String,
//! }
//!
//! let mut original = MyStruct {
//! number: 0,
//! another_number: 0,
//! text: "nice".to_string(),
//! };
//!
//! let opt = Opt {
//! number: MyWrapper::Nothing,
//! another_number: MyWrapper::NothingElse,
//! text: MyWrapper::Something("great".to_string()),
//! };
//!
//! original.merge_opt(opt);
//!
//! assert_eq!(original.number, 0);
//! assert_eq!(original.another_number, 0);
//! assert_eq!(original.text, "great".to_string());
//! ```
//! Furthermore, if any of the original fields are already wrapped in the custom
//! wrapper and you are **not** using `rewrap`, a
//! `From<&MyWrapper<T>> for Option<()>` is required. This is needed in order to
//! figure out whether to merge the value without turning it into an `Option`
//! and losing its type information. If `Option::<()>::from(opt.field)` is
//! `Some(())` then the value in the original struct instance will be replaced
//! with the one from the wrapped struct and otherwise ignored. Example:
//! ```
//! # use optfield::*;
//! #[derive(Debug, PartialEq)]
//! enum MyWrapper<T> {
//! Nothing,
//! Something(T),
//! }
//!
//! impl<T> From<MyWrapper<T>> for Option<T> {
//! fn from(value: MyWrapper<T>) -> Option<T> {
//! match value {
//! MyWrapper::Nothing => None,
//! MyWrapper::Something(t) => Some(t),
//! }
//! }
//! }
//!
//! impl<T> From<&MyWrapper<T>> for Option<()> {
//! fn from(value: &MyWrapper<T>) -> Self {
//! match value {
//! MyWrapper::Nothing => None,
//! MyWrapper::Something(_) => Some(()),
//! }
//! }
//! }
//!
//! #[optfield(Opt, wrapper = MyWrapper, merge_fn)]
//! struct MyStruct {
//! number: i32,
//! wrapped: MyWrapper<i32>,
//! }
//!
//! let mut original = MyStruct {
//! number: 0,
//! wrapped: MyWrapper::Something(0),
//! };
//!
//! let opt = Opt {
//! number: MyWrapper::Something(42),
//! wrapped: MyWrapper::Something(1337),
//! };
//!
//! original.merge_opt(opt);
//!
//! assert_eq!(original.number, 42);
//! assert_eq!(original.wrapped, MyWrapper::Something(1337));
//! ```
//! ### `wrapper` + `from`
//! When using `from` a `From<T> for MyWrapper<T>` implementation must exist,
//! either generic or specific for each of the target struct field types:
//! ```
//! # use optfield::*;
//! #[derive(Debug, PartialEq)]
//! enum MyWrapper<T> {
//! Nothing,
//! Something(T),
//! }
//!
//! impl<T> From<T> for MyWrapper<T> {
//! fn from(value: T) -> Self {
//! MyWrapper::Something(value)
//! }
//! }
//!
//! #[optfield(Opt, wrapper = MyWrapper, from)]
//! struct MyStruct {
//! field: i32,
//! }
//!
//! let original = MyStruct { field: 88 };
//!
//! let opt = Opt::from(original);
//!
//! assert_eq!(opt.field, MyWrapper::Something(88));
//! ```
//! ### Wrapper nesting
//! With its support for attributes optfield can be nested. This may be useful
//! to combine various wrappers.
//! ```
//! # use optfield::*;
//! struct MyWrapper<T>(T);
//!
//! #[optfield(OptMyWrapper, wrapper = MyWrapper, attrs = (
//! optfield(OptOption)
//! ))]
//! struct MyStruct {
//! number: i32,
//! text: String,
//! }
//! ```
//! Will generate:
//! ```
//! # struct MyWrapper<T>(T);
//! struct OptMyWrapper {
//! number: MyWrapper<i32>,
//! text: MyWrapper<String>,
//! }
//!
//! struct OptOption {
//! number: Option<MyWrapper<i32>>,
//! text: Option<MyWrapper<String>>,
//! }
//! ```
extern crate proc_macro;

use crate::proc_macro::TokenStream;
Expand Down

0 comments on commit 06cb9e6

Please sign in to comment.