mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 14:33:37 -08:00
chore(API, AppServer): Deal with CryptoServer being uninit.
Before this, we would just raise an error.
This commit is contained in:
@@ -8,6 +8,7 @@ use mio::Interest;
|
|||||||
use mio::Token;
|
use mio::Token;
|
||||||
use rosenpass_secret_memory::Public;
|
use rosenpass_secret_memory::Public;
|
||||||
use rosenpass_secret_memory::Secret;
|
use rosenpass_secret_memory::Secret;
|
||||||
|
use rosenpass_util::build::ConstructionSite;
|
||||||
use rosenpass_util::file::StoreValueB64;
|
use rosenpass_util::file::StoreValueB64;
|
||||||
use rosenpass_wireguard_broker::WireguardBrokerMio;
|
use rosenpass_wireguard_broker::WireguardBrokerMio;
|
||||||
use rosenpass_wireguard_broker::{WireguardBrokerCfg, WG_KEY_LEN};
|
use rosenpass_wireguard_broker::{WireguardBrokerCfg, WG_KEY_LEN};
|
||||||
@@ -31,6 +32,7 @@ use std::slice;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crate::protocol::BuildCryptoServer;
|
||||||
use crate::protocol::HostIdentification;
|
use crate::protocol::HostIdentification;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Verbosity,
|
config::Verbosity,
|
||||||
@@ -147,7 +149,7 @@ pub struct AppServerTest {
|
|||||||
// TODO add user control via unix domain socket and stdin/stdout
|
// TODO add user control via unix domain socket and stdin/stdout
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppServer {
|
pub struct AppServer {
|
||||||
pub crypt: Option<CryptoServer>,
|
pub crypto_site: ConstructionSite<BuildCryptoServer, CryptoServer>,
|
||||||
pub sockets: Vec<mio::net::UdpSocket>,
|
pub sockets: Vec<mio::net::UdpSocket>,
|
||||||
pub events: mio::Events,
|
pub events: mio::Events,
|
||||||
pub mio_poll: mio::Poll,
|
pub mio_poll: mio::Poll,
|
||||||
@@ -606,7 +608,7 @@ impl AppServer {
|
|||||||
// TODO use mio::net::UnixStream together with std::os::unix::net::UnixStream for Linux
|
// TODO use mio::net::UnixStream together with std::os::unix::net::UnixStream for Linux
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
crypt: Some(CryptoServer::new(sk, pk)),
|
crypto_site: ConstructionSite::from_product(CryptoServer::new(sk, pk)),
|
||||||
peers: Vec::new(),
|
peers: Vec::new(),
|
||||||
verbosity,
|
verbosity,
|
||||||
sockets,
|
sockets,
|
||||||
@@ -627,14 +629,14 @@ impl AppServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn crypto_server(&self) -> anyhow::Result<&CryptoServer> {
|
pub fn crypto_server(&self) -> anyhow::Result<&CryptoServer> {
|
||||||
self.crypt
|
self.crypto_site
|
||||||
.as_ref()
|
.product_ref()
|
||||||
.context("Cryptography handler not initialized")
|
.context("Cryptography handler not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crypto_server_mut(&mut self) -> anyhow::Result<&mut CryptoServer> {
|
pub fn crypto_server_mut(&mut self) -> anyhow::Result<&mut CryptoServer> {
|
||||||
self.crypt
|
self.crypto_site
|
||||||
.as_mut()
|
.product_mut()
|
||||||
.context("Cryptography handler not initialized")
|
.context("Cryptography handler not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,8 +690,13 @@ impl AppServer {
|
|||||||
broker_peer: Option<BrokerPeer>,
|
broker_peer: Option<BrokerPeer>,
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
) -> anyhow::Result<AppPeerPtr> {
|
) -> anyhow::Result<AppPeerPtr> {
|
||||||
let PeerPtr(pn) = self.crypto_server_mut()?.add_peer(psk, pk)?;
|
let PeerPtr(pn) = match &mut self.crypto_site {
|
||||||
|
ConstructionSite::Void => bail!("Crypto server construction site is void"),
|
||||||
|
ConstructionSite::Builder(builder) => builder.add_peer(psk, pk),
|
||||||
|
ConstructionSite::Product(srv) => srv.add_peer(psk, pk)?,
|
||||||
|
};
|
||||||
assert!(pn == self.peers.len());
|
assert!(pn == self.peers.len());
|
||||||
|
|
||||||
let initial_endpoint = hostname
|
let initial_endpoint = hostname
|
||||||
.map(Endpoint::discovery_from_hostname)
|
.map(Endpoint::discovery_from_hostname)
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
@@ -774,16 +781,31 @@ impl AppServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.poll(&mut *rx)? {
|
enum CryptoSrv {
|
||||||
#[allow(clippy::redundant_closure_call)]
|
Avail,
|
||||||
SendInitiation(peer) => tx_maybe_with!(peer, || self
|
Missing,
|
||||||
|
}
|
||||||
|
|
||||||
|
let poll_result = self.poll(&mut *rx)?;
|
||||||
|
let have_crypto = match self.crypto_site.is_available() {
|
||||||
|
true => CryptoSrv::Avail,
|
||||||
|
false => CryptoSrv::Missing,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::redundant_closure_call)]
|
||||||
|
match (have_crypto, poll_result) {
|
||||||
|
(CryptoSrv::Missing, SendInitiation(_)) => {}
|
||||||
|
(CryptoSrv::Avail, SendInitiation(peer)) => tx_maybe_with!(peer, || self
|
||||||
.crypto_server_mut()?
|
.crypto_server_mut()?
|
||||||
.initiate_handshake(peer.lower(), &mut *tx))?,
|
.initiate_handshake(peer.lower(), &mut *tx))?,
|
||||||
#[allow(clippy::redundant_closure_call)]
|
|
||||||
SendRetransmission(peer) => tx_maybe_with!(peer, || self
|
(CryptoSrv::Missing, SendRetransmission(_)) => {}
|
||||||
|
(CryptoSrv::Avail, SendRetransmission(peer)) => tx_maybe_with!(peer, || self
|
||||||
.crypto_server_mut()?
|
.crypto_server_mut()?
|
||||||
.retransmit_handshake(peer.lower(), &mut *tx))?,
|
.retransmit_handshake(peer.lower(), &mut *tx))?,
|
||||||
DeleteKey(peer) => {
|
|
||||||
|
(CryptoSrv::Missing, DeleteKey(_)) => {}
|
||||||
|
(CryptoSrv::Avail, DeleteKey(peer)) => {
|
||||||
self.output_key(peer, Stale, &SymKey::random())?;
|
self.output_key(peer, Stale, &SymKey::random())?;
|
||||||
|
|
||||||
// There was a loss of connection apparently; restart host discovery
|
// There was a loss of connection apparently; restart host discovery
|
||||||
@@ -797,7 +819,8 @@ impl AppServer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceivedMessage(len, endpoint) => {
|
(CryptoSrv::Missing, ReceivedMessage(_, _)) => {}
|
||||||
|
(CryptoSrv::Avail, ReceivedMessage(len, endpoint)) => {
|
||||||
let msg_result = match self.under_load {
|
let msg_result = match self.under_load {
|
||||||
DoSOperation::UnderLoad => {
|
DoSOperation::UnderLoad => {
|
||||||
self.handle_msg_under_load(&endpoint, &rx[..len], &mut *tx)
|
self.handle_msg_under_load(&endpoint, &rx[..len], &mut *tx)
|
||||||
@@ -910,17 +933,32 @@ impl AppServer {
|
|||||||
pub fn poll(&mut self, rx_buf: &mut [u8]) -> anyhow::Result<AppPollResult> {
|
pub fn poll(&mut self, rx_buf: &mut [u8]) -> anyhow::Result<AppPollResult> {
|
||||||
use crate::protocol::PollResult as C;
|
use crate::protocol::PollResult as C;
|
||||||
use AppPollResult as A;
|
use AppPollResult as A;
|
||||||
loop {
|
let res = loop {
|
||||||
return Ok(match self.crypto_server_mut()?.poll()? {
|
// Call CryptoServer's poll (if available)
|
||||||
C::DeleteKey(PeerPtr(no)) => A::DeleteKey(AppPeerPtr(no)),
|
let crypto_poll = self
|
||||||
C::SendInitiation(PeerPtr(no)) => A::SendInitiation(AppPeerPtr(no)),
|
.crypto_site
|
||||||
C::SendRetransmission(PeerPtr(no)) => A::SendRetransmission(AppPeerPtr(no)),
|
.product_mut()
|
||||||
C::Sleep(timeout) => match self.try_recv(rx_buf, timeout)? {
|
.map(|crypto| crypto.poll())
|
||||||
Some((len, addr)) => A::ReceivedMessage(len, addr),
|
.transpose()?;
|
||||||
None => continue,
|
|
||||||
},
|
// Map crypto server's poll result to our poll result
|
||||||
});
|
let io_poll_timeout = match crypto_poll {
|
||||||
}
|
Some(C::DeleteKey(PeerPtr(no))) => break A::DeleteKey(AppPeerPtr(no)),
|
||||||
|
Some(C::SendInitiation(PeerPtr(no))) => break A::SendInitiation(AppPeerPtr(no)),
|
||||||
|
Some(C::SendRetransmission(PeerPtr(no))) => {
|
||||||
|
break A::SendRetransmission(AppPeerPtr(no))
|
||||||
|
}
|
||||||
|
Some(C::Sleep(timeout)) => timeout, // No event from crypto-server, do IO
|
||||||
|
None => crate::protocol::UNENDING, // Crypto server is uninitialized, do IO
|
||||||
|
};
|
||||||
|
|
||||||
|
// Perform IO (look for a message)
|
||||||
|
if let Some((len, addr)) = self.try_recv(rx_buf, io_poll_timeout)? {
|
||||||
|
break A::ReceivedMessage(len, addr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to receive a new message
|
/// Tries to receive a new message
|
||||||
|
|||||||
127
rosenpass/src/protocol/build_crypto_server.rs
Normal file
127
rosenpass/src/protocol/build_crypto_server.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
use rosenpass_util::{
|
||||||
|
build::Build,
|
||||||
|
mem::{DiscardResultExt, SwapWithDefaultExt},
|
||||||
|
result::ensure_or,
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Keypair {
|
||||||
|
pub sk: SSk,
|
||||||
|
pub pk: SPk,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We need a named tuple derive
|
||||||
|
impl Keypair {
|
||||||
|
pub fn new(sk: SSk, pk: SPk) -> Self {
|
||||||
|
Self { sk, pk }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Self::new(SSk::zero(), SPk::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random() -> Self {
|
||||||
|
Self::new(SSk::random(), SPk::random())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_parts(parts: (SSk, SPk)) -> Self {
|
||||||
|
Self::new(parts.0, parts.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_parts(self) -> (SSk, SPk) {
|
||||||
|
(self.sk, self.pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("PSK already set in BuildCryptoServer")]
|
||||||
|
pub struct PskAlreadySet;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("Keypair already set in BuildCryptoServer")]
|
||||||
|
pub struct KeypairAlreadySet;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("Can not construct CryptoServer: Missing keypair")]
|
||||||
|
pub struct MissingKeypair;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct BuildCryptoServer {
|
||||||
|
pub keypair: Option<Keypair>,
|
||||||
|
pub peers: Vec<PeerParams>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Build<CryptoServer> for BuildCryptoServer {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn build(self) -> Result<CryptoServer, Self::Error> {
|
||||||
|
let Some(Keypair { sk, pk }) = self.keypair else {
|
||||||
|
return Err(MissingKeypair)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut srv = CryptoServer::new(sk, pk);
|
||||||
|
|
||||||
|
for (idx, PeerParams { psk, pk }) in self.peers.into_iter().enumerate() {
|
||||||
|
let PeerPtr(idx2) = srv.add_peer(psk, pk)?;
|
||||||
|
assert!(idx == idx2, "Peer id changed during CryptoServer construction from {idx} to {idx2}. This is a developer error.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(srv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PeerParams {
|
||||||
|
pub psk: Option<SymKey>,
|
||||||
|
pub pk: SPk,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildCryptoServer {
|
||||||
|
pub fn new(keypair: Option<Keypair>, peers: Vec<PeerParams>) -> Self {
|
||||||
|
Self { keypair, peers }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self::new(None, Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_parts(parts: (Option<Keypair>, Vec<PeerParams>)) -> Self {
|
||||||
|
Self {
|
||||||
|
keypair: parts.0,
|
||||||
|
peers: parts.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_parts(&mut self) -> (Option<Keypair>, Vec<PeerParams>) {
|
||||||
|
(self.keypair.take(), self.peers.swap_with_default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_parts(mut self) -> (Option<Keypair>, Vec<PeerParams>) {
|
||||||
|
self.take_parts()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_keypair(&mut self, keypair: Keypair) -> Result<&mut Self, KeypairAlreadySet> {
|
||||||
|
ensure_or(self.keypair.is_none(), KeypairAlreadySet)?;
|
||||||
|
self.keypair.insert(keypair).discard_result();
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_added_peer(&mut self, psk: Option<SymKey>, pk: SPk) -> &mut Self {
|
||||||
|
// TODO: Check here already whether peer was already added
|
||||||
|
self.peers.push(PeerParams { psk, pk });
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_peer(&mut self, psk: Option<SymKey>, pk: SPk) -> PeerPtr {
|
||||||
|
let id = PeerPtr(self.peers.len());
|
||||||
|
self.with_added_peer(psk, pk);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emancipate(&mut self) -> Self {
|
||||||
|
Self::from_parts(self.take_parts())
|
||||||
|
}
|
||||||
|
}
|
||||||
6
rosenpass/src/protocol/mod.rs
Normal file
6
rosenpass/src/protocol/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
mod build_crypto_server;
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
|
mod protocol;
|
||||||
|
|
||||||
|
pub use build_crypto_server::*;
|
||||||
|
pub use protocol::*;
|
||||||
169
util/src/build.rs
Normal file
169
util/src/build.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use crate::{
|
||||||
|
functional::ApplyExt,
|
||||||
|
mem::{SwapWithDefaultExt, SwapWithExt},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum ConstructionSiteErectError<E> {
|
||||||
|
#[error("Construction site is void")]
|
||||||
|
IsVoid,
|
||||||
|
#[error("Construction is already built")]
|
||||||
|
AlreadyBuilt,
|
||||||
|
#[error("Other construction site error {0:?}")]
|
||||||
|
Other(#[from] E),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Build<T>: Sized {
|
||||||
|
type Error;
|
||||||
|
fn build(self) -> Result<T, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ConstructionSite<Builder, T>
|
||||||
|
where
|
||||||
|
Builder: Build<T>,
|
||||||
|
{
|
||||||
|
Void,
|
||||||
|
Builder(Builder),
|
||||||
|
Product(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Builder, T> Default for ConstructionSite<Builder, T>
|
||||||
|
where
|
||||||
|
Builder: Build<T>,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Builder, T> ConstructionSite<Builder, T>
|
||||||
|
where
|
||||||
|
Builder: Build<T>,
|
||||||
|
{
|
||||||
|
pub fn void() -> Self {
|
||||||
|
Self::Void
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(builder: Builder) -> Self {
|
||||||
|
Self::Builder(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_product(value: T) -> Self {
|
||||||
|
Self::Product(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self) -> Self {
|
||||||
|
self.swap_with_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modify_taken_with_return<R, F>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(Self) -> (Self, R),
|
||||||
|
{
|
||||||
|
let (site, res) = self.take().apply(f);
|
||||||
|
self.swap_with(site);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modify_taken<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(Self) -> Self,
|
||||||
|
{
|
||||||
|
self.take().apply(f).swap_with_mut(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
|
pub fn erect(&mut self) -> Result<(), ConstructionSiteErectError<Builder::Error>> {
|
||||||
|
self.modify_taken_with_return(|site| {
|
||||||
|
let builder = match site {
|
||||||
|
site @ Self::Void => return (site, Err(ConstructionSiteErectError::IsVoid)),
|
||||||
|
site @ Self::Product(_) => {
|
||||||
|
return (site, Err(ConstructionSiteErectError::AlreadyBuilt))
|
||||||
|
}
|
||||||
|
Self::Builder(builder) => builder,
|
||||||
|
};
|
||||||
|
|
||||||
|
let product = match builder.build() {
|
||||||
|
Err(e) => {
|
||||||
|
return (Self::void(), Err(ConstructionSiteErectError::Other(e)));
|
||||||
|
}
|
||||||
|
Ok(p) => p,
|
||||||
|
};
|
||||||
|
|
||||||
|
(Self::from_product(product), Ok(()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the construction site is [`Void`].
|
||||||
|
///
|
||||||
|
/// [`Void`]: ConstructionSite::Void
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_void(&self) -> bool {
|
||||||
|
matches!(self, Self::Void)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the construction site is [`InProgress`].
|
||||||
|
///
|
||||||
|
/// [`InProgress`]: ConstructionSite::InProgress
|
||||||
|
#[must_use]
|
||||||
|
pub fn in_progess(&self) -> bool {
|
||||||
|
matches!(self, Self::Builder(..))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the construction site is [`Done`].
|
||||||
|
///
|
||||||
|
/// [`Done`]: ConstructionSite::Done
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_available(&self) -> bool {
|
||||||
|
matches!(self, Self::Product(..))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_builder(self) -> Option<Builder> {
|
||||||
|
use ConstructionSite as S;
|
||||||
|
match self {
|
||||||
|
S::Builder(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder_ref(&self) -> Option<&Builder> {
|
||||||
|
use ConstructionSite as S;
|
||||||
|
match self {
|
||||||
|
S::Builder(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder_mut(&mut self) -> Option<&mut Builder> {
|
||||||
|
use ConstructionSite as S;
|
||||||
|
match self {
|
||||||
|
S::Builder(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_product(self) -> Option<T> {
|
||||||
|
use ConstructionSite as S;
|
||||||
|
match self {
|
||||||
|
S::Product(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn product_ref(&self) -> Option<&T> {
|
||||||
|
use ConstructionSite as S;
|
||||||
|
match self {
|
||||||
|
S::Product(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn product_mut(&mut self) -> Option<&mut T> {
|
||||||
|
use ConstructionSite as S;
|
||||||
|
match self {
|
||||||
|
S::Product(v) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,32 @@ where
|
|||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait MutatingExt {
|
||||||
|
fn mutating<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Self);
|
||||||
|
fn mutating_mut<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MutatingExt for T {
|
||||||
|
fn mutating<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Self),
|
||||||
|
{
|
||||||
|
mutating(self, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mutating_mut<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Self),
|
||||||
|
{
|
||||||
|
f(self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sideeffect<T, F>(v: T, f: F) -> T
|
pub fn sideeffect<T, F>(v: T, f: F) -> T
|
||||||
where
|
where
|
||||||
F: Fn(&T),
|
F: Fn(&T),
|
||||||
@@ -14,6 +40,58 @@ where
|
|||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait SideffectExt {
|
||||||
|
fn sideeffect<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&Self);
|
||||||
|
fn sideeffect_ref<F>(&self, f: F) -> &Self
|
||||||
|
where
|
||||||
|
F: Fn(&Self);
|
||||||
|
fn sideeffect_mut<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: Fn(&Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SideffectExt for T {
|
||||||
|
fn sideeffect<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&Self),
|
||||||
|
{
|
||||||
|
sideeffect(self, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sideeffect_ref<F>(&self, f: F) -> &Self
|
||||||
|
where
|
||||||
|
F: Fn(&Self),
|
||||||
|
{
|
||||||
|
f(self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sideeffect_mut<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: Fn(&Self),
|
||||||
|
{
|
||||||
|
f(self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run<R, F: FnOnce() -> R>(f: F) -> R {
|
pub fn run<R, F: FnOnce() -> R>(f: F) -> R {
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ApplyExt: Sized {
|
||||||
|
fn apply<R, F>(self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(Self) -> R;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sized> ApplyExt for T {
|
||||||
|
fn apply<R, F>(self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(Self) -> R,
|
||||||
|
{
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
pub mod b64;
|
pub mod b64;
|
||||||
|
pub mod build;
|
||||||
pub mod fd;
|
pub mod fd;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod functional;
|
pub mod functional;
|
||||||
|
|||||||
@@ -92,3 +92,47 @@ impl<T> Drop for Forgetting<T> {
|
|||||||
forget(value)
|
forget(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait DiscardResultExt {
|
||||||
|
fn discard_result(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DiscardResultExt for T {
|
||||||
|
fn discard_result(self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ForgetExt {
|
||||||
|
fn forget(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ForgetExt for T {
|
||||||
|
fn forget(self) {
|
||||||
|
std::mem::forget(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SwapWithExt {
|
||||||
|
fn swap_with(&mut self, other: Self) -> Self;
|
||||||
|
fn swap_with_mut(&mut self, other: &mut Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SwapWithExt for T {
|
||||||
|
fn swap_with(&mut self, mut other: Self) -> Self {
|
||||||
|
self.swap_with_mut(&mut other);
|
||||||
|
other
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap_with_mut(&mut self, other: &mut Self) {
|
||||||
|
std::mem::swap(self, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SwapWithDefaultExt {
|
||||||
|
fn swap_with_default(&mut self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> SwapWithDefaultExt for T {
|
||||||
|
fn swap_with_default(&mut self) -> Self {
|
||||||
|
self.swap_with(Self::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ macro_rules! attempt {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait OkExt<E>: Sized {
|
||||||
|
fn ok(self) -> Result<Self, E>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> OkExt<E> for T {
|
||||||
|
fn ok(self) -> Result<Self, E> {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Trait for container types that guarantee successful unwrapping.
|
/// Trait for container types that guarantee successful unwrapping.
|
||||||
///
|
///
|
||||||
/// The `.guaranteed()` function can be used over unwrap to show that
|
/// The `.guaranteed()` function can be used over unwrap to show that
|
||||||
|
|||||||
Reference in New Issue
Block a user