// Copyright © 2019-2020 The Radicle Foundation use std::{ collections::BTreeMap, convert::TryFrom, iter::FromIterator, ops::{Deref, DerefMut}, }; use crypto::{PublicKey, ssh}; use metadata::commit::{ CommitData, headers::Signature::{Pgp, Ssh}, }; pub use ssh::ExtendedSignature; pub mod error; // FIXME(kim): This should really be a HashMap with a no-op Hasher -- PublicKey // collisions are catastrophic #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Signatures(BTreeMap); impl Deref for Signatures { type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Signatures { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for Signatures { fn from(ExtendedSignature { key, sig }: ExtendedSignature) -> Self { let mut map = BTreeMap::new(); map.insert(key, sig); map.into() } } impl From> for Signatures { fn from(map: BTreeMap) -> Self { Self(map) } } impl From for BTreeMap { fn from(s: Signatures) -> Self { s.0 } } impl TryFrom<&CommitData> for Signatures { type Error = error::Signatures; fn try_from(value: &CommitData) -> Result { value .signatures() .filter_map(|signature| { match signature { // Skip PGP signatures Pgp(_) => None, Ssh(pem) => Some( ExtendedSignature::from_pem(pem.as_bytes()) .map_err(error::Signatures::from), ), } }) .map(|r| r.map(|es| (es.key, es.sig))) .collect::>() } } impl FromIterator<(PublicKey, crypto::Signature)> for Signatures { fn from_iter(iter: T) -> Self where T: IntoIterator, { Self(BTreeMap::from_iter(iter)) } } impl IntoIterator for Signatures { type Item = (PublicKey, crypto::Signature); type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Extend for Signatures { fn extend(&mut self, iter: T) where T: IntoIterator, { for ExtendedSignature { key, sig } in iter { self.insert(key, sig); } } } impl Extend<(PublicKey, crypto::Signature)> for Signatures { fn extend(&mut self, iter: T) where T: IntoIterator, { for (key, sig) in iter { self.insert(key, sig); } } }