diff --git a/core/src/upgrade/from_fn.rs b/core/src/upgrade/from_fn.rs new file mode 100644 index 00000000..c6ef52c1 --- /dev/null +++ b/core/src/upgrade/from_fn.rs @@ -0,0 +1,109 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::{Endpoint, upgrade::{InboundUpgrade, OutboundUpgrade, ProtocolName, UpgradeInfo}}; + +use futures::prelude::*; +use std::iter; + +/// Initializes a new [`FromFnUpgrade`]. +/// +/// # Example +/// +/// ``` +/// # use libp2p_core::transport::{Transport, MemoryTransport}; +/// # use libp2p_core::upgrade; +/// # use std::io; +/// let _transport = MemoryTransport::default() +/// .and_then(move |out, cp| { +/// upgrade::apply(out, upgrade::from_fn("/foo/1", move |mut sock, endpoint| async move { +/// if endpoint.is_dialer() { +/// upgrade::write_one(&mut sock, "some handshake data").await?; +/// } else { +/// let handshake_data = upgrade::read_one(&mut sock, 1024).await?; +/// if handshake_data != b"some handshake data" { +/// return Err(upgrade::ReadOneError::from(io::Error::from(io::ErrorKind::Other))); +/// } +/// } +/// Ok(sock) +/// }), cp, upgrade::Version::V1) +/// }); +/// ``` +/// +pub fn from_fn(protocol_name: P, fun: F) -> FromFnUpgrade +where + // Note: these bounds are there in order to help the compiler infer types + P: ProtocolName + Clone, + F: FnOnce(C, Endpoint) -> Fut, + Fut: Future>, +{ + FromFnUpgrade { protocol_name, fun } +} + +/// Implements the `UpgradeInfo`, `InboundUpgrade` and `OutboundUpgrade` traits. +/// +/// The upgrade consists in calling the function passed when creating this struct. +#[derive(Debug, Clone)] +pub struct FromFnUpgrade { + protocol_name: P, + fun: F, +} + +impl UpgradeInfo for FromFnUpgrade +where + P: ProtocolName + Clone, +{ + type Info = P; + type InfoIter = iter::Once

; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(self.protocol_name.clone()) + } +} + +impl InboundUpgrade for FromFnUpgrade +where + P: ProtocolName + Clone, + F: FnOnce(C, Endpoint) -> Fut, + Fut: Future>, +{ + type Output = Out; + type Error = Err; + type Future = Fut; + + fn upgrade_inbound(self, sock: C, _: Self::Info) -> Self::Future { + (self.fun)(sock, Endpoint::Listener) + } +} + +impl OutboundUpgrade for FromFnUpgrade +where + P: ProtocolName + Clone, + F: FnOnce(C, Endpoint) -> Fut, + Fut: Future>, +{ + type Output = Out; + type Error = Err; + type Future = Fut; + + fn upgrade_outbound(self, sock: C, _: Self::Info) -> Self::Future { + (self.fun)(sock, Endpoint::Dialer) + } +} diff --git a/core/src/upgrade/mod.rs b/core/src/upgrade/mod.rs index cc870693..dbe3a5b7 100644 --- a/core/src/upgrade/mod.rs +++ b/core/src/upgrade/mod.rs @@ -61,6 +61,7 @@ mod apply; mod denied; mod either; mod error; +mod from_fn; mod map; mod optional; mod select; @@ -75,6 +76,7 @@ pub use self::{ denied::DeniedUpgrade, either::EitherUpgrade, error::UpgradeError, + from_fn::{from_fn, FromFnUpgrade}, map::{MapInboundUpgrade, MapOutboundUpgrade, MapInboundUpgradeErr, MapOutboundUpgradeErr}, optional::OptionalUpgrade, select::SelectUpgrade,