diff options
-rw-r--r-- | openpgp/NEWS | 1 | ||||
-rw-r--r-- | openpgp/src/packet/any.rs | 158 | ||||
-rw-r--r-- | openpgp/src/packet/mod.rs | 3 | ||||
-rw-r--r-- | openpgp/src/packet/prelude.rs | 1 |
4 files changed, 163 insertions, 0 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS index a66dae21..c458e9bc 100644 --- a/openpgp/NEWS +++ b/openpgp/NEWS @@ -4,6 +4,7 @@ * Changes in 1.9.0 ** New functionality + - packet::Any - Packet::version - SignatureBuilder::set_reference_time - SignatureBuilder::effective_signature_creation_time diff --git a/openpgp/src/packet/any.rs b/openpgp/src/packet/any.rs new file mode 100644 index 00000000..8af73eaf --- /dev/null +++ b/openpgp/src/packet/any.rs @@ -0,0 +1,158 @@ +//! Convenient downcasting from Packets to Packet Bodies. + +use crate::packet::{ + Packet, + Unknown, + Signature, + OnePassSig, + key::{ + PublicKey, + PublicSubkey, + SecretKey, + SecretSubkey, + }, + Marker, + Trust, + UserID, + UserAttribute, + Literal, + CompressedData, + PKESK, + SKESK, + SEIP, + MDC, + AED, +}; + +/// Convenient downcasting from Packets to Packet Bodies. +/// +/// This trait offers functionality similar to [`std::any::Any`], +/// hence the name. +/// +/// # Sealed trait +/// +/// This trait is [sealed] and cannot be implemented for types outside +/// this crate. Therefore it can be extended in a non-breaking way. +/// +/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed +pub trait Any<T>: crate::seal::Sealed { + /// Attempts to downcast to `T`, returning the packet if it fails. + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp::packet::prelude::*; + /// let p: Packet = Marker::default().into(); + /// let m: Marker = p.downcast().unwrap(); + /// # let _ = m; + /// ``` + fn downcast(self) -> std::result::Result<T, Packet>; + + /// Attempts to downcast to `&T`, returning `None` if it fails. + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp::packet::prelude::*; + /// let p: Packet = Marker::default().into(); + /// let m: &Marker = p.downcast_ref().unwrap(); + /// # let _ = m; + /// ``` + fn downcast_ref(&self) -> Option<&T>; + + /// Attempts to downcast to `&mut T`, returning `None` if it fails. + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp::packet::prelude::*; + /// let mut p: Packet = Marker::default().into(); + /// let m: &mut Marker = p.downcast_mut().unwrap(); + /// # let _ = m; + /// ``` + fn downcast_mut(&mut self) -> Option<&mut T>; +} + +macro_rules! impl_downcast_for { + ($typ: tt) => { + impl Any<$typ> for Packet { + fn downcast(self) -> std::result::Result<$typ, Packet> { + match self { + Packet::$typ(v) => Ok(v), + p => Err(p), + } + } + + fn downcast_ref(&self) -> Option<&$typ> { + match self { + Packet::$typ(v) => Some(v), + _ => None, + } + } + + fn downcast_mut(&mut self) -> Option<&mut $typ> { + match self { + Packet::$typ(v) => Some(v), + _ => None, + } + } + } + }; +} + +macro_rules! impl_downcasts { + ($($typ:ident, )*) => { + $(impl_downcast_for!($typ);)* + + /// Checks that all packet types have implementations of `Any`. + /// + /// Not visible outside this module, isn't supposed to be + /// called, this is a compile-time check. + #[allow(unused)] + fn check_exhaustion(p: Packet) { + match p { + $(Packet::$typ(_) => (),)* + } + } + } +} + +impl_downcasts!( + Unknown, + Signature, + OnePassSig, + PublicKey, + PublicSubkey, + SecretKey, + SecretSubkey, + Marker, + Trust, + UserID, + UserAttribute, + Literal, + CompressedData, + PKESK, + SKESK, + SEIP, + MDC, + AED, +); + + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn downcast() { + let p: Packet = Marker::default().into(); + let mut p = Any::<UserID>::downcast(p).unwrap_err(); + let r: Option<&UserID> = p.downcast_ref(); + assert!(r.is_none()); + let r: Option<&mut UserID> = p.downcast_mut(); + assert!(r.is_none()); + let _: &Marker = p.downcast_ref().unwrap(); + let _: &mut Marker = p.downcast_mut().unwrap(); + let _: Marker = p.downcast().unwrap(); + } +} diff --git a/openpgp/src/packet/mod.rs b/openpgp/src/packet/mod.rs index 991d3e38..b112eaf2 100644 --- a/openpgp/src/packet/mod.rs +++ b/openpgp/src/packet/mod.rs @@ -173,6 +173,9 @@ use crate::crypto::{ Password, }; +mod any; +pub use self::any::Any; + mod tag; pub use self::tag::Tag; pub mod header; diff --git a/openpgp/src/packet/prelude.rs b/openpgp/src/packet/prelude.rs index 74b96284..3a1e6d88 100644 --- a/openpgp/src/packet/prelude.rs +++ b/openpgp/src/packet/prelude.rs @@ -22,6 +22,7 @@ pub use crate::packet::{ AED, + Any, Body, CompressedData, Container, |