mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 06:23:08 -08:00
Compare commits
1 Commits
dev/update
...
dev/flake-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00b1020f0a |
534
Cargo.lock
generated
534
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@ memsec = { git = "https://github.com/rosenpass/memsec.git", rev = "aceb9baee8aec
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
typenum = "1.17.0"
|
typenum = "1.17.0"
|
||||||
log = { version = "0.4.22" }
|
log = { version = "0.4.22" }
|
||||||
clap = { version = "4.5.20", features = ["derive"] }
|
clap = { version = "4.5.19", features = ["derive"] }
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||||
anyhow = { version = "1.0.89", features = ["backtrace", "std"] }
|
anyhow = { version = "1.0.89", features = ["backtrace", "std"] }
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
//! Constant-time comparison
|
|
||||||
|
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
/// Little endian memcmp version of quinier/memsec
|
/// Little endian memcmp version of quinier/memsec
|
||||||
/// https://github.com/quininer/memsec/blob/bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0/src/lib.rs#L30
|
/// https://github.com/quininer/memsec/blob/bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0/src/lib.rs#L30
|
||||||
///
|
|
||||||
/// # Panic & Safety
|
|
||||||
///
|
|
||||||
/// Both input arrays must be at least of the indicated length.
|
|
||||||
///
|
|
||||||
/// See [std::ptr::read_volatile] on safety.
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub unsafe fn memcmp_le(b1: *const u8, b2: *const u8, len: usize) -> i32 {
|
pub unsafe fn memcmp_le(b1: *const u8, b2: *const u8, len: usize) -> i32 {
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
@@ -21,16 +13,6 @@ pub unsafe fn memcmp_le(b1: *const u8, b2: *const u8, len: usize) -> i32 {
|
|||||||
((res - 1) >> 8) + (res >> 8) + 1
|
((res - 1) >> 8) + (res >> 8) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn memcmp_le_test() {
|
|
||||||
// use rosenpass_constant_time::memcmp_le;
|
|
||||||
let a = [0, 1, 0, 0];
|
|
||||||
let b = [0, 0, 0, 1];
|
|
||||||
assert_eq!(-1, unsafe { memcmp_le(a.as_ptr(), b.as_ptr(), 4) });
|
|
||||||
assert_eq!(0, unsafe { memcmp_le(a.as_ptr(), a.as_ptr(), 4) });
|
|
||||||
assert_eq!(1, unsafe { memcmp_le(b.as_ptr(), a.as_ptr(), 4) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// compares two slices of memory content and returns an integer indicating the relationship between
|
/// compares two slices of memory content and returns an integer indicating the relationship between
|
||||||
/// the slices
|
/// the slices
|
||||||
///
|
///
|
||||||
@@ -50,28 +32,6 @@ pub fn memcmp_le_test() {
|
|||||||
/// ## Tests
|
/// ## Tests
|
||||||
/// For discussion on how to ensure the constant-time execution of this function, see
|
/// For discussion on how to ensure the constant-time execution of this function, see
|
||||||
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use rosenpass_constant_time::compare;
|
|
||||||
/// let a = [0, 1, 0, 0];
|
|
||||||
/// let b = [0, 0, 0, 1];
|
|
||||||
/// assert_eq!(-1, compare(&a, &b));
|
|
||||||
/// assert_eq!(0, compare(&a, &a));
|
|
||||||
/// assert_eq!(1, compare(&b, &a));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Panic
|
|
||||||
///
|
|
||||||
/// This function will panic if the input arrays are of different lengths.
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use rosenpass_constant_time::compare;
|
|
||||||
/// let a = [0, 1, 0];
|
|
||||||
/// let b = [0, 0, 0, 1];
|
|
||||||
/// compare(&a, &b);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn compare(a: &[u8], b: &[u8]) -> i32 {
|
pub fn compare(a: &[u8], b: &[u8]) -> i32 {
|
||||||
assert!(a.len() == b.len());
|
assert!(a.len() == b.len());
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//! Incrementing numbers
|
|
||||||
|
|
||||||
use core::hint::black_box;
|
use core::hint::black_box;
|
||||||
|
|
||||||
/// Interpret the given slice as a little-endian unsigned integer
|
/// Interpret the given slice as a little-endian unsigned integer
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(clippy::missing_docs_in_private_items)]
|
|
||||||
//! constant-time implementations of some primitives
|
//! constant-time implementations of some primitives
|
||||||
//!
|
//!
|
||||||
//! Rosenpass internal library providing basic constant-time operations.
|
//! Rosenpass internal library providing basic constant-time operations.
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//! memcmp
|
|
||||||
|
|
||||||
/// compares two sclices of memory content and returns whether they are equal
|
/// compares two sclices of memory content and returns whether they are equal
|
||||||
///
|
///
|
||||||
/// ## Leaks
|
/// ## Leaks
|
||||||
@@ -9,18 +7,6 @@
|
|||||||
///
|
///
|
||||||
/// The execution time of the function grows approx. linear with the length of the input. This is
|
/// The execution time of the function grows approx. linear with the length of the input. This is
|
||||||
/// considered safe.
|
/// considered safe.
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use rosenpass_constant_time::memcmp;
|
|
||||||
/// let a = [0, 0, 0, 0];
|
|
||||||
/// let b = [0, 0, 0, 1];
|
|
||||||
/// let c = [0, 0, 0];
|
|
||||||
/// assert!(memcmp(&a, &a));
|
|
||||||
/// assert!(!memcmp(&a, &b));
|
|
||||||
/// assert!(!memcmp(&a, &c));
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
|
pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
|
||||||
a.len() == b.len() && unsafe { memsec::memeq(a.as_ptr(), b.as_ptr(), a.len()) }
|
a.len() == b.len() && unsafe { memsec::memeq(a.as_ptr(), b.as_ptr(), a.len()) }
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//! xor
|
|
||||||
|
|
||||||
use core::hint::black_box;
|
use core::hint::black_box;
|
||||||
use rosenpass_to::{with_destination, To};
|
use rosenpass_to::{with_destination, To};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,3 @@ rosenpass-cipher-traits = { workspace = true }
|
|||||||
rosenpass-util = { workspace = true }
|
rosenpass-util = { workspace = true }
|
||||||
oqs-sys = { workspace = true }
|
oqs-sys = { workspace = true }
|
||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rosenpass-secret-memory = { workspace = true }
|
|
||||||
rosenpass-constant-time = { workspace = true }
|
|
||||||
|
|||||||
@@ -1,42 +1,9 @@
|
|||||||
//! Generic helpers for declaring bindings to liboqs kems
|
|
||||||
|
|
||||||
/// Generate bindings to a liboqs-provided KEM
|
|
||||||
macro_rules! oqs_kem {
|
macro_rules! oqs_kem {
|
||||||
($name:ident) => { ::paste::paste!{
|
($name:ident) => { ::paste::paste!{
|
||||||
#[doc = "Bindings for ::oqs_sys::kem::" [<"OQS_KEM" _ $name:snake>] "_*"]
|
|
||||||
mod [< $name:snake >] {
|
mod [< $name:snake >] {
|
||||||
use rosenpass_cipher_traits::Kem;
|
use rosenpass_cipher_traits::Kem;
|
||||||
use rosenpass_util::result::Guaranteed;
|
use rosenpass_util::result::Guaranteed;
|
||||||
|
|
||||||
#[doc = "Bindings for ::oqs_sys::kem::" [<"OQS_KEM" _ $name:snake>] "_*"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "# Examples"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "```rust"]
|
|
||||||
#[doc = "use std::borrow::{Borrow, BorrowMut};"]
|
|
||||||
#[doc = "use rosenpass_cipher_traits::Kem;"]
|
|
||||||
#[doc = "use rosenpass_oqs::" $name:camel " as MyKem;"]
|
|
||||||
#[doc = "use rosenpass_secret_memory::{Secret, Public};"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "// Recipient generates secret key, transfers pk to sender"]
|
|
||||||
#[doc = "let mut sk = Secret::<{ MyKem::SK_LEN }>::zero();"]
|
|
||||||
#[doc = "let mut pk = Public::<{ MyKem::PK_LEN }>::zero();"]
|
|
||||||
#[doc = "MyKem::keygen(sk.secret_mut(), pk.borrow_mut());"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "// Sender generates ciphertext and local shared key, sends ciphertext to recipient"]
|
|
||||||
#[doc = "let mut shk_enc = Secret::<{ MyKem::SHK_LEN }>::zero();"]
|
|
||||||
#[doc = "let mut ct = Public::<{ MyKem::CT_LEN }>::zero();"]
|
|
||||||
#[doc = "MyKem::encaps(shk_enc.secret_mut(), ct.borrow_mut(), pk.borrow());"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "// Recipient decapsulates ciphertext"]
|
|
||||||
#[doc = "let mut shk_dec = Secret::<{ MyKem::SHK_LEN }>::zero();"]
|
|
||||||
#[doc = "MyKem::decaps(shk_dec.secret_mut(), sk.secret(), ct.borrow());"]
|
|
||||||
#[doc = ""]
|
|
||||||
#[doc = "// Both parties end up with the same shared key"]
|
|
||||||
#[doc = "assert!(rosenpass_constant_time::compare(shk_enc.secret_mut(), shk_dec.secret_mut()) == 0);"]
|
|
||||||
#[doc = "```"]
|
|
||||||
pub enum [< $name:camel >] {}
|
pub enum [< $name:camel >] {}
|
||||||
|
|
||||||
/// # Panic & Safety
|
/// # Panic & Safety
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(clippy::missing_docs_in_private_items)]
|
|
||||||
//! Bindings for liboqs used in Rosenpass
|
|
||||||
|
|
||||||
/// Call into a libOQS function
|
|
||||||
macro_rules! oqs_call {
|
macro_rules! oqs_call {
|
||||||
($name:path, $($args:expr),*) => {{
|
($name:path, $($args:expr),*) => {{
|
||||||
use oqs_sys::common::OQS_STATUS::*;
|
use oqs_sys::common::OQS_STATUS::*;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rosenpass"
|
name = "rosenpass"
|
||||||
version = "0.3.0-dev"
|
version = "0.2.1"
|
||||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ pub struct AppServerTest {
|
|||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum AppServerIoSource {
|
pub enum AppServerIoSource {
|
||||||
Socket(usize),
|
Socket(usize),
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
PskBroker(Public<BROKER_ID_BYTES>),
|
PskBroker(Public<BROKER_ID_BYTES>),
|
||||||
#[cfg(feature = "experiment_api")]
|
#[cfg(feature = "experiment_api")]
|
||||||
MioManager(crate::api::mio::MioManagerIoSource),
|
MioManager(crate::api::mio::MioManagerIoSource),
|
||||||
@@ -1208,12 +1209,15 @@ impl AppServer {
|
|||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
io_source: AppServerIoSource,
|
io_source: AppServerIoSource,
|
||||||
) -> anyhow::Result<Option<(usize, Endpoint)>> {
|
) -> anyhow::Result<Option<(usize, Endpoint)>> {
|
||||||
|
use crate::api::mio::MioManagerContext;
|
||||||
|
|
||||||
match io_source {
|
match io_source {
|
||||||
AppServerIoSource::Socket(idx) => self
|
AppServerIoSource::Socket(idx) => self
|
||||||
.try_recv_from_listen_socket(buf, idx)
|
.try_recv_from_listen_socket(buf, idx)
|
||||||
.substitute_for_ioerr_wouldblock(None)?
|
.substitute_for_ioerr_wouldblock(None)?
|
||||||
.ok(),
|
.ok(),
|
||||||
|
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
AppServerIoSource::PskBroker(key) => self
|
AppServerIoSource::PskBroker(key) => self
|
||||||
.brokers
|
.brokers
|
||||||
.store
|
.store
|
||||||
@@ -1223,13 +1227,9 @@ impl AppServer {
|
|||||||
.map(|_| None),
|
.map(|_| None),
|
||||||
|
|
||||||
#[cfg(feature = "experiment_api")]
|
#[cfg(feature = "experiment_api")]
|
||||||
AppServerIoSource::MioManager(mmio_src) => {
|
AppServerIoSource::MioManager(mmio_src) => MioManagerFocus(self)
|
||||||
use crate::api::mio::MioManagerContext;
|
|
||||||
|
|
||||||
MioManagerFocus(self)
|
|
||||||
.poll_particular(mmio_src)
|
.poll_particular(mmio_src)
|
||||||
.map(|_| None)
|
.map(|_| None),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
//! Utilities for working with Base64
|
|
||||||
|
|
||||||
use base64ct::{Base64, Decoder as B64Reader, Encoder as B64Writer};
|
use base64ct::{Base64, Decoder as B64Reader, Encoder as B64Writer};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
/// Formatter that displays its input as base64.
|
|
||||||
///
|
|
||||||
/// Use through [B64Display].
|
|
||||||
pub struct B64DisplayHelper<'a, const F: usize>(&'a [u8]);
|
pub struct B64DisplayHelper<'a, const F: usize>(&'a [u8]);
|
||||||
|
|
||||||
impl<const F: usize> Display for B64DisplayHelper<'_, F> {
|
impl<const F: usize> Display for B64DisplayHelper<'_, F> {
|
||||||
@@ -20,25 +15,7 @@ impl<const F: usize> Display for B64DisplayHelper<'_, F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension trait that can be used to display values as Base64
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::b64::B64Display;
|
|
||||||
///
|
|
||||||
/// let a = vec![0,1,2,3,4,5];
|
|
||||||
/// assert_eq!(
|
|
||||||
/// format!("{}", a.fmt_b64::<10>()), // Maximum size of the encoded buffer
|
|
||||||
/// "AAECAwQF",
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub trait B64Display {
|
pub trait B64Display {
|
||||||
/// Display this value as base64
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See [B64Display].
|
|
||||||
fn fmt_b64<const F: usize>(&self) -> B64DisplayHelper<F>;
|
fn fmt_b64<const F: usize>(&self) -> B64DisplayHelper<F>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,11 +31,6 @@ impl<T: AsRef<[u8]>> B64Display for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a base64-encoded value
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See [b64_encode].
|
|
||||||
pub fn b64_decode(input: &[u8], output: &mut [u8]) -> anyhow::Result<()> {
|
pub fn b64_decode(input: &[u8], output: &mut [u8]) -> anyhow::Result<()> {
|
||||||
let mut reader = B64Reader::<Base64>::new(input).map_err(|e| anyhow::anyhow!(e))?;
|
let mut reader = B64Reader::<Base64>::new(input).map_err(|e| anyhow::anyhow!(e))?;
|
||||||
match reader.decode(output) {
|
match reader.decode(output) {
|
||||||
@@ -77,23 +49,6 @@ pub fn b64_decode(input: &[u8], output: &mut [u8]) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode a value as base64.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::b64::{b64_encode, b64_decode};
|
|
||||||
///
|
|
||||||
/// let bytes = b"Hello World";
|
|
||||||
///
|
|
||||||
/// let mut encoder_buffer = [0u8; 64];
|
|
||||||
/// let encoded = b64_encode(bytes, &mut encoder_buffer)?;
|
|
||||||
///
|
|
||||||
/// let mut bytes_decoded = [0u8; 11];
|
|
||||||
/// b64_decode(encoded.as_bytes(), &mut bytes_decoded);
|
|
||||||
/// assert_eq!(bytes, &bytes_decoded);
|
|
||||||
///
|
|
||||||
/// Ok::<(), anyhow::Error>(())
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn b64_encode<'o>(input: &[u8], output: &'o mut [u8]) -> anyhow::Result<&'o str> {
|
pub fn b64_encode<'o>(input: &[u8], output: &'o mut [u8]) -> anyhow::Result<&'o str> {
|
||||||
let mut writer = B64Writer::<Base64>::new(output).map_err(|e| anyhow::anyhow!(e))?;
|
let mut writer = B64Writer::<Base64>::new(output).map_err(|e| anyhow::anyhow!(e))?;
|
||||||
writer.encode(input).map_err(|e| anyhow::anyhow!(e))?;
|
writer.encode(input).map_err(|e| anyhow::anyhow!(e))?;
|
||||||
|
|||||||
@@ -1,163 +1,33 @@
|
|||||||
//! Lazy construction of values
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
functional::ApplyExt,
|
functional::ApplyExt,
|
||||||
mem::{SwapWithDefaultExt, SwapWithExt},
|
mem::{SwapWithDefaultExt, SwapWithExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Errors returned by [ConstructionSite::erect]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[derive(thiserror::Error, Debug, Eq, PartialEq)]
|
|
||||||
pub enum ConstructionSiteErectError<E> {
|
pub enum ConstructionSiteErectError<E> {
|
||||||
/// Attempted to erect an empty construction site
|
|
||||||
#[error("Construction site is void")]
|
#[error("Construction site is void")]
|
||||||
IsVoid,
|
IsVoid,
|
||||||
/// Attempted to erect a construction that is already standing
|
|
||||||
#[error("Construction is already built")]
|
#[error("Construction is already built")]
|
||||||
AlreadyBuilt,
|
AlreadyBuilt,
|
||||||
/// Other error
|
|
||||||
#[error("Other construction site error {0:?}")]
|
#[error("Other construction site error {0:?}")]
|
||||||
Other(#[from] E),
|
Other(#[from] E),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that can build some other type
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::Build;
|
|
||||||
/// use anyhow::{Context, Result};
|
|
||||||
///
|
|
||||||
/// #[derive(Eq, PartialEq, Debug)]
|
|
||||||
/// struct Person {
|
|
||||||
/// pub fav_pokemon: String,
|
|
||||||
/// pub fav_number: u8,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Default, Clone)]
|
|
||||||
/// struct PersonBuilder {
|
|
||||||
/// pub fav_pokemon: Option<String>,
|
|
||||||
/// pub fav_number: Option<u8>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl Build<Person> for &PersonBuilder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<Person, Self::Error> {
|
|
||||||
/// let fav_pokemon = self.fav_pokemon.clone().context("Missing fav pokemon")?;
|
|
||||||
/// let fav_number = self.fav_number.context("Missing fav number")?;
|
|
||||||
/// Ok(Person {
|
|
||||||
/// fav_pokemon,
|
|
||||||
/// fav_number,
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut person_builder = PersonBuilder::default();
|
|
||||||
/// assert!(person_builder.build().is_err());
|
|
||||||
///
|
|
||||||
/// person_builder.fav_pokemon = Some("Krabby".to_owned());
|
|
||||||
/// person_builder.fav_number = Some(0);
|
|
||||||
/// assert_eq!(
|
|
||||||
/// person_builder.build().unwrap(),
|
|
||||||
/// Person {
|
|
||||||
/// fav_pokemon: "Krabby".to_owned(),
|
|
||||||
/// fav_number: 0
|
|
||||||
/// }
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub trait Build<T>: Sized {
|
pub trait Build<T>: Sized {
|
||||||
/// Error returned by the builder
|
|
||||||
type Error;
|
type Error;
|
||||||
/// Build the type
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See [Self].
|
|
||||||
fn build(self) -> Result<T, Self::Error>;
|
fn build(self) -> Result<T, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that can be incrementally built from a type that can [Build] it
|
#[derive(Debug)]
|
||||||
///
|
|
||||||
/// This is similar to an option, where [Self::Void] is [std::Option::None],
|
|
||||||
/// [Self::Product] is [std::Option::Some], except that there is a third
|
|
||||||
/// intermediate state [Self::Builder] that represents a Some/Product value
|
|
||||||
/// in the process of being made.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::borrow::Borrow;
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
/// use anyhow::{Context, Result};
|
|
||||||
///
|
|
||||||
/// #[derive(Eq, PartialEq, Debug)]
|
|
||||||
/// struct Person {
|
|
||||||
/// pub fav_pokemon: String,
|
|
||||||
/// pub fav_number: u8,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Eq, PartialEq, Default, Clone, Debug)]
|
|
||||||
/// struct PersonBuilder {
|
|
||||||
/// pub fav_pokemon: Option<String>,
|
|
||||||
/// pub fav_number: Option<u8>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl Build<Person> for &PersonBuilder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<Person, Self::Error> {
|
|
||||||
/// let fav_pokemon = self.fav_pokemon.clone().context("Missing fav pokemon")?;
|
|
||||||
/// let fav_number = self.fav_number.context("Missing fav number")?;
|
|
||||||
/// Ok(Person {
|
|
||||||
/// fav_pokemon,
|
|
||||||
/// fav_number,
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl Build<Person> for PersonBuilder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<Person, Self::Error> {
|
|
||||||
/// self.borrow().build()
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Allocate the construction site
|
|
||||||
/// let mut site = ConstructionSite::void();
|
|
||||||
///
|
|
||||||
/// // Start construction
|
|
||||||
/// site = ConstructionSite::Builder(PersonBuilder::default());
|
|
||||||
///
|
|
||||||
/// // Use the builder to build the value
|
|
||||||
/// site.builder_mut().unwrap().fav_pokemon = Some("Krabby".to_owned());
|
|
||||||
/// site.builder_mut().unwrap().fav_number = Some(0);
|
|
||||||
///
|
|
||||||
/// // Use `erect` to call Build::build
|
|
||||||
/// site.erect();
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// site,
|
|
||||||
/// ConstructionSite::Product(Person {
|
|
||||||
/// fav_pokemon: "Krabby".to_owned(),
|
|
||||||
/// fav_number: 0
|
|
||||||
/// }),
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
|
||||||
pub enum ConstructionSite<Builder, T>
|
pub enum ConstructionSite<Builder, T>
|
||||||
where
|
where
|
||||||
Builder: Build<T>,
|
Builder: Build<T>,
|
||||||
{
|
{
|
||||||
/// The site is empty
|
|
||||||
Void,
|
Void,
|
||||||
/// The site is being built
|
|
||||||
Builder(Builder),
|
Builder(Builder),
|
||||||
/// The site has been built and is now finished
|
|
||||||
Product(T),
|
Product(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the construction site as [ConstructionSite::Void]
|
|
||||||
impl<Builder, T> Default for ConstructionSite<Builder, T>
|
impl<Builder, T> Default for ConstructionSite<Builder, T>
|
||||||
where
|
where
|
||||||
Builder: Build<T>,
|
Builder: Build<T>,
|
||||||
@@ -171,189 +41,22 @@ impl<Builder, T> ConstructionSite<Builder, T>
|
|||||||
where
|
where
|
||||||
Builder: Build<T>,
|
Builder: Build<T>,
|
||||||
{
|
{
|
||||||
/// Initializes the construction site as [ConstructionSite::Void]
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See [Self].
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// ConstructionSite::<Builder, House>::void(),
|
|
||||||
/// ConstructionSite::Void,
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn void() -> Self {
|
pub fn void() -> Self {
|
||||||
Self::Void
|
Self::Void
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the construction site from its builder
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// ConstructionSite::<Builder, House>::new(Builder),
|
|
||||||
/// ConstructionSite::Builder(Builder),
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn new(builder: Builder) -> Self {
|
pub fn new(builder: Builder) -> Self {
|
||||||
Self::Builder(builder)
|
Self::Builder(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the construction site from its product
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// ConstructionSite::<Builder, House>::from_product(House),
|
|
||||||
/// ConstructionSite::Product(House),
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn from_product(value: T) -> Self {
|
pub fn from_product(value: T) -> Self {
|
||||||
Self::Product(value)
|
Self::Product(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the construction site and replace it with [Self::Void]
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::<Builder, House>::from_product(House);
|
|
||||||
/// let a_backup = a.clone();
|
|
||||||
///
|
|
||||||
/// let b = a.take();
|
|
||||||
/// assert_eq!(a, ConstructionSite::void());
|
|
||||||
/// assert_eq!(b, ConstructionSite::Product(House));
|
|
||||||
/// ```
|
|
||||||
pub fn take(&mut self) -> Self {
|
pub fn take(&mut self) -> Self {
|
||||||
self.swap_with_default()
|
self.swap_with_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the given function to Self, temporarily converting
|
|
||||||
/// the mutable reference into an owned value.
|
|
||||||
///
|
|
||||||
/// This is useful if you have some function that needs to modify
|
|
||||||
/// the construction site as an owned value but all you have is a reference.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House(u32);
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder(u32);
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House(self.0))
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, PartialEq, Eq)]
|
|
||||||
/// enum FancyMatchState {
|
|
||||||
/// New,
|
|
||||||
/// Built,
|
|
||||||
/// Increment,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// fn fancy_match(site: &mut ConstructionSite<Builder, House>, def: u32) -> FancyMatchState {
|
|
||||||
/// site.modify_taken_with_return(|site| {
|
|
||||||
/// use ConstructionSite as C;
|
|
||||||
/// use FancyMatchState as F;
|
|
||||||
/// let (prod, state) = match site {
|
|
||||||
/// C::Void => (House(def), F::New),
|
|
||||||
/// C::Builder(b) => (b.build().unwrap(), F::Built),
|
|
||||||
/// C::Product(House(v)) => (House(v + 1), F::Increment),
|
|
||||||
/// };
|
|
||||||
/// let prod = ConstructionSite::from_product(prod);
|
|
||||||
/// (prod, state)
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::void();
|
|
||||||
/// let r = fancy_match(&mut a, 42);
|
|
||||||
/// assert_eq!(a, ConstructionSite::Product(House(42)));
|
|
||||||
/// assert_eq!(r, FancyMatchState::New);
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::new(Builder(13));
|
|
||||||
/// let r = fancy_match(&mut a, 42);
|
|
||||||
/// assert_eq!(a, ConstructionSite::Product(House(13)));
|
|
||||||
/// assert_eq!(r, FancyMatchState::Built);
|
|
||||||
///
|
|
||||||
/// let r = fancy_match(&mut a, 42);
|
|
||||||
/// assert_eq!(a, ConstructionSite::Product(House(14)));
|
|
||||||
/// assert_eq!(r, FancyMatchState::Increment);
|
|
||||||
/// ```
|
|
||||||
pub fn modify_taken_with_return<R, F>(&mut self, f: F) -> R
|
pub fn modify_taken_with_return<R, F>(&mut self, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(Self) -> (Self, R),
|
F: FnOnce(Self) -> (Self, R),
|
||||||
@@ -363,53 +66,6 @@ where
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the given function to Self, temporarily converting
|
|
||||||
/// the mutable reference into an owned value.
|
|
||||||
///
|
|
||||||
/// This is useful if you have some function that needs to modify
|
|
||||||
/// the construction site as an owned value but all you have is a reference.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House(u32);
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder(u32);
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House(self.0))
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn fancy_match(site: &mut ConstructionSite<Builder, House>, def: u32) {
|
|
||||||
/// site.modify_taken(|site| {
|
|
||||||
/// use ConstructionSite as C;
|
|
||||||
/// let prod = match site {
|
|
||||||
/// C::Void => House(def),
|
|
||||||
/// C::Builder(b) => b.build().unwrap(),
|
|
||||||
/// C::Product(House(v)) => House(v + 1),
|
|
||||||
/// };
|
|
||||||
/// ConstructionSite::from_product(prod)
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::void();
|
|
||||||
/// fancy_match(&mut a, 42);
|
|
||||||
/// assert_eq!(a, ConstructionSite::Product(House(42)));
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::new(Builder(13));
|
|
||||||
/// fancy_match(&mut a, 42);
|
|
||||||
/// assert_eq!(a, ConstructionSite::Product(House(13)));
|
|
||||||
///
|
|
||||||
/// fancy_match(&mut a, 42);
|
|
||||||
/// assert_eq!(a, ConstructionSite::Product(House(14)));
|
|
||||||
/// ```
|
|
||||||
pub fn modify_taken<F>(&mut self, f: F)
|
pub fn modify_taken<F>(&mut self, f: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(Self) -> Self,
|
F: FnOnce(Self) -> Self,
|
||||||
@@ -417,42 +73,6 @@ where
|
|||||||
self.take().apply(f).swap_with_mut(self)
|
self.take().apply(f).swap_with_mut(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this constructions site contains [Self::Builder], call the inner [Build]'s [Build::build]
|
|
||||||
/// and have the construction site contain a product.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See [Self].
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build, ConstructionSiteErectError};
|
|
||||||
/// use std::convert::Infallible;
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = Infallible;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::<Builder, House>::void();
|
|
||||||
/// assert_eq!(a.erect(), Err(ConstructionSiteErectError::IsVoid));
|
|
||||||
/// assert_eq!(a, ConstructionSite::void());
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::<Builder, House>::from_product(House);
|
|
||||||
/// assert_eq!(a.erect(), Err(ConstructionSiteErectError::AlreadyBuilt));
|
|
||||||
/// assert_eq!(a, ConstructionSite::from_product(House));
|
|
||||||
///
|
|
||||||
/// let mut a = ConstructionSite::<Builder, House>::new(Builder);
|
|
||||||
/// a.erect().unwrap();
|
|
||||||
/// assert_eq!(a, ConstructionSite::from_product(House));
|
|
||||||
/// ```
|
|
||||||
#[allow(clippy::result_unit_err)]
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn erect(&mut self) -> Result<(), ConstructionSiteErectError<Builder::Error>> {
|
pub fn erect(&mut self) -> Result<(), ConstructionSiteErectError<Builder::Error>> {
|
||||||
self.modify_taken_with_return(|site| {
|
self.modify_taken_with_return(|site| {
|
||||||
@@ -478,31 +98,6 @@ where
|
|||||||
/// Returns `true` if the construction site is [`Void`].
|
/// Returns `true` if the construction site is [`Void`].
|
||||||
///
|
///
|
||||||
/// [`Void`]: ConstructionSite::Void
|
/// [`Void`]: ConstructionSite::Void
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// type Site = ConstructionSite<Builder, House>;
|
|
||||||
///
|
|
||||||
/// assert_eq!(Site::Void.is_void(), true);
|
|
||||||
/// assert_eq!(Site::Builder(Builder).is_void(), false);
|
|
||||||
/// assert_eq!(Site::Product(House).is_void(), false);
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_void(&self) -> bool {
|
pub fn is_void(&self) -> bool {
|
||||||
matches!(self, Self::Void)
|
matches!(self, Self::Void)
|
||||||
@@ -511,95 +106,19 @@ where
|
|||||||
/// Returns `true` if the construction site is [`InProgress`].
|
/// Returns `true` if the construction site is [`InProgress`].
|
||||||
///
|
///
|
||||||
/// [`InProgress`]: ConstructionSite::InProgress
|
/// [`InProgress`]: ConstructionSite::InProgress
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// type Site = ConstructionSite<Builder, House>;
|
|
||||||
///
|
|
||||||
/// assert_eq!(Site::Void.in_progress(), false);
|
|
||||||
/// assert_eq!(Site::Builder(Builder).in_progress(), true);
|
|
||||||
/// assert_eq!(Site::Product(House).in_progress(), false);
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn in_progress(&self) -> bool {
|
pub fn in_progess(&self) -> bool {
|
||||||
matches!(self, Self::Builder(..))
|
matches!(self, Self::Builder(..))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the construction site is [`Done`].
|
/// Returns `true` if the construction site is [`Done`].
|
||||||
///
|
///
|
||||||
/// [`Done`]: ConstructionSite::Done
|
/// [`Done`]: ConstructionSite::Done
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// type Site = ConstructionSite<Builder, House>;
|
|
||||||
///
|
|
||||||
/// assert_eq!(Site::Void.is_available(), false);
|
|
||||||
/// assert_eq!(Site::Builder(Builder).is_available(), false);
|
|
||||||
/// assert_eq!(Site::Product(House).is_available(), true);
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_available(&self) -> bool {
|
pub fn is_available(&self) -> bool {
|
||||||
matches!(self, Self::Product(..))
|
matches!(self, Self::Product(..))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value of [Self::Builder]
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rosenpass_util::build::{ConstructionSite, Build};
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct House;
|
|
||||||
/// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
||||||
/// struct Builder;
|
|
||||||
///
|
|
||||||
/// impl Build<House> for Builder {
|
|
||||||
/// type Error = anyhow::Error;
|
|
||||||
///
|
|
||||||
/// fn build(self) -> Result<House, Self::Error> {
|
|
||||||
/// Ok(House)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// type Site = ConstructionSite<Builder, House>;
|
|
||||||
///
|
|
||||||
/// assert_eq!(Site::Void.into_builder(), None);
|
|
||||||
/// assert_eq!(Site::Builder(Builder).into_builder(), Some(Builder));
|
|
||||||
/// assert_eq!(Site::Product(House).into_builder(), None);
|
|
||||||
/// ```
|
|
||||||
pub fn into_builder(self) -> Option<Builder> {
|
pub fn into_builder(self) -> Option<Builder> {
|
||||||
use ConstructionSite as S;
|
use ConstructionSite as S;
|
||||||
match self {
|
match self {
|
||||||
@@ -608,11 +127,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value of [Self::Builder] as a reference
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See [Self::into_builder].
|
|
||||||
pub fn builder_ref(&self) -> Option<&Builder> {
|
pub fn builder_ref(&self) -> Option<&Builder> {
|
||||||
use ConstructionSite as S;
|
use ConstructionSite as S;
|
||||||
match self {
|
match self {
|
||||||
@@ -621,11 +135,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value of [Self::Builder] as a mutable reference
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Similar to [Self::into_builder].
|
|
||||||
pub fn builder_mut(&mut self) -> Option<&mut Builder> {
|
pub fn builder_mut(&mut self) -> Option<&mut Builder> {
|
||||||
use ConstructionSite as S;
|
use ConstructionSite as S;
|
||||||
match self {
|
match self {
|
||||||
@@ -634,11 +143,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value of [Self::Product]
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Similar to [Self::into_builder].
|
|
||||||
pub fn into_product(self) -> Option<T> {
|
pub fn into_product(self) -> Option<T> {
|
||||||
use ConstructionSite as S;
|
use ConstructionSite as S;
|
||||||
match self {
|
match self {
|
||||||
@@ -647,11 +151,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value of [Self::Product] as a reference
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Similar to [Self::into_builder].
|
|
||||||
pub fn product_ref(&self) -> Option<&T> {
|
pub fn product_ref(&self) -> Option<&T> {
|
||||||
use ConstructionSite as S;
|
use ConstructionSite as S;
|
||||||
match self {
|
match self {
|
||||||
@@ -660,11 +159,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value of [Self::Product] as a mutable reference
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Similar to [Self::into_builder].
|
|
||||||
pub fn product_mut(&mut self) -> Option<&mut T> {
|
pub fn product_mut(&mut self) -> Option<&mut T> {
|
||||||
use ConstructionSite as S;
|
use ConstructionSite as S;
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(clippy::missing_docs_in_private_items)]
|
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
pub mod b64;
|
pub mod b64;
|
||||||
|
|||||||
Reference in New Issue
Block a user