relax 0.1.1

Derive Partial<T>
Documentation
//! Derive partial (“relaxed”) types
//!
//! # Usage
//!
//! ## Generate “relaxed” types
//!
//! ```
//! use relax::Relax;
//!
//! #[derive(Relax)]
//! #[relax(PartialData)]
//! pub(crate) struct Data {
//!     id: u16,
//!     pub name: String,
//!     pub favorite: Option<String>,
//! }
//! ```
//!
//! yields the following partial (“relaxed”) struct:
//!
//! ```
//! pub(crate) struct PartialData {
//!     id: Option<u16>,
//!     pub name: Option<String>,
//!     pub favorite: Option<String>,
//! }
//! ```
//!
//! Nesting is also supported:
//!
//! ```
//! use relax::Relax;
//!
//! #[derive(Relax)]
//! #[relax(PartialFoo)]
//! struct Foo {
//!     id: u16,
//!     #[relax]
//!     bar: Bar,
//!     #[relax]
//!     bar2: Option<Bar>,
//! }
//!
//! #[derive(Relax)]
//! #[relax(PartialBar)]
//! struct Bar {
//!     name: String,
//!     favorite: Option<String>,
//! }
//! ```
//!
//! yields
//!
//! ```
//! struct PatrialFoo {
//!     id: Option<u16>,
//!     bar: Option<PartialBar>,
//!     bar2: Option<PartialBar>,
//! }
//!
//! struct PartialBar {
//!     name: Option<String>,
//!     favorite: Option<String>,
//! }
//! ```
//!
//! You can also add attributes to generated structs:
//!
//! ```
//! use relax::Relax;
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Relax)]
//! #[relax(PartialData, derive(Serialize, Deserialize), serde(rename_all = "UPPERCASE"))]
//! pub(crate) struct Data {
//!     id: u16,
//!     pub name: String,
//!     pub favorite: Option<String>,
//! }
//! ```
//!
//! yields
//!
//! ```ignore
//! #[derive(Serialize, Deserialize)]
//! #[serde(rename_all = "UPPERCASE")]
//! pub(crate) struct PartialData {
//!     id: Option<u16>,
//!     pub name: Option<String>,
//!     pub favorite: Option<String>,
//! }
//! ```
//!
//! ## Create empty “relaxed” instances
//!
//! Relaxed structs derive [`Relaxed`] trait, which has [`new`](Relaxed::new) associated function.
//!
//! ```
//! use relax::{Relax, Relaxed};
//!
//! #[derive(Relax)]
//! #[relax(PartialData, derive(Debug, PartialEq))]
//! pub(crate) struct Data {
//!     id: u16,
//!     pub name: String,
//!     pub favorite: Option<String>,
//! }
//!
//! # fn main() {
//! let empty = PartialData {
//!     id: None,
//!     name: None,
//!     favorite: None,
//! };
//!
//! assert_eq!(PartialData::new(), empty);
//! # }
//! ```
//!
//! ## Combine multiple “relaxed” instances
//!
//! [`Relaxed`] trait also has [`merge`](Relaxed::merge) function. The behavior is similar to [`Option::or`], except that this function is applied for each struct field.
//!
//! ```
//! use relax::{Relax, Relaxed};
//!
//! #[derive(Relax)]
//! #[relax(PartialData, derive(Debug, PartialEq))]
//! pub(crate) struct Data {
//!     id: u16,
//!     pub name: String,
//!     pub favorite: Option<String>,
//! }
//!
//! # fn main() {
//!
//! let partial_data_1 = PartialData {
//!     id: None,
//!     name: Some("John".to_owned()),
//!     favorite: None,
//! };
//!
//! let partial_data_2 = PartialData {
//!     id: None,
//!     name: None,
//!     favorite: Some("Rust".to_owned()),
//! };
//!
//! let merged = partial_data_1.merge(partial_data_2);
//!
//! let expected = PartialData {
//!     id: None,
//!     name: Some("John".to_owned()),
//!     favorite: Some("Rust".to_owned()),
//! };
//!
//! assert_eq!(merged, expected);
//!
//! # }
//! ```
//!
//! ## Create an original struct instance from a relaxed instance
//!
//! If all the required fields of the original struct are set in a relaxed struct instance, you can use [`try_into()`](std::convert::TryInto::try_into) to convert it to the original struct.
//!
//! ```ignore
//! use relax::{Relax, Relaxed};
//!
//! let partial_data_1: PartialData = read_data(file_1);
//! let partial_data_2: PartialData = read_data(file_2);
//! let partial_data_3: PartialData = read_data(file_3);
//!
//! let data: Data = partial_data_1
//!                     .merge(partial_data_2)
//!                     .merge(partial_data_3)
//!                     .try_into()?;
//! ```

#[cfg(feature = "derive")]
pub use relax_macros::Relax;

use std::convert::{From, TryFrom};

pub trait Relax: TryFrom<Self::Partial, Error = RequiredFieldNotSet> {
    type Partial;
}

pub trait Relaxed: From<Self::Complete> {
    type Complete;

    fn new() -> Self;
    fn merge(self, optb: Self) -> Self;
}

#[derive(Debug)]
pub struct RequiredFieldNotSet(pub &'static str);

impl std::fmt::Display for RequiredFieldNotSet {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "required field is not set ({})", self.0)
    }
}
impl std::error::Error for RequiredFieldNotSet {}