use std::collections::HashSet; use std::str::FromStr; use clap::builder::ValueParserFactory; use itertools::Itertools; use models::FromStrParser; use serde::{Deserialize, Serialize}; use ts_rs::TS; use url::Url; use crate::prelude::*; use crate::sign::commitment::Digestable; use crate::sign::{AnySignature, AnyVerifyingKey, SignatureScheme}; #[derive(Debug, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] #[model = "Model"] #[ts(export)] pub struct SignerInfo { pub name: String, pub contact: Vec, pub keys: HashSet, } #[derive(Clone, Debug, Deserialize, Serialize, TS, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[ts(export)] // TODO: better types pub enum ContactInfo { Email(String), Matrix(String), Website(#[ts(type = "string")] Url), } impl std::fmt::Display for ContactInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Email(e) => write!(f, "mailto:{e}"), Self::Matrix(m) => write!(f, "https://matrix.to/#/{m}"), Self::Website(w) => write!(f, "{w}"), } } } impl FromStr for ContactInfo { type Err = Error; fn from_str(s: &str) -> Result { Ok(if let Some(s) = s.strip_prefix("mailto:") { Self::Email(s.to_owned()) } else if let Some(s) = s.strip_prefix("https://matrix.to/#/") { Self::Matrix(s.to_owned()) } else { Self::Website(s.parse()?) }) } } impl ValueParserFactory for ContactInfo { type Parser = FromStrParser; fn value_parser() -> Self::Parser { Self::Parser::new() } } #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub enum AcceptSigners { #[serde(skip)] Accepted, Signer(AnyVerifyingKey), Any(Vec), All(Vec), } impl AcceptSigners { const fn null() -> Self { Self::Any(Vec::new()) } pub fn flatten(self) -> Self { match self { Self::Any(mut s) | Self::All(mut s) if s.len() == 1 => s.swap_remove(0).flatten(), s => s, } } pub fn accepted(&self) -> bool { match self { Self::Accepted => true, _ => false, } } pub fn try_accept(self) -> Result<(), Error> { if self.accepted() { Ok(()) } else { Err(Error::new( eyre!("signer(s) not accepted"), ErrorKind::InvalidSignature, )) } } pub fn process_signature( &mut self, signer: &AnyVerifyingKey, commitment: &impl Digestable, context: &str, signature: &AnySignature, ) -> Result<(), Error> { let mut res = Ok(()); let new = match std::mem::replace(self, Self::null()) { Self::Accepted => Self::Accepted, Self::Signer(s) => { if &s == signer { res = signer .scheme() .verify_commitment(signer, commitment, context, signature); Self::Accepted } else { Self::Signer(s) } } Self::All(mut s) => { res = s .iter_mut() .map(|s| s.process_signature(signer, commitment, context, signature)) .collect(); if s.iter().all(|s| s.accepted()) { Self::Accepted } else { Self::All(s) } } Self::Any(mut s) => { match s .iter_mut() .map(|s| { s.process_signature(signer, commitment, context, signature)?; Ok(s) }) .filter_ok(|s| s.accepted()) .next() { Some(Ok(s)) => std::mem::replace(s, Self::null()), Some(Err(e)) => { res = Err(e); Self::Any(s) } None => Self::Any(s), } } }; *self = new; res } }