use chrono::{DateTime, Utc}; use crate::{ ALGORITHM_FIELD, ALGORITHM_VALUE, CREATED_FIELD, EXPIRES_FIELD, HEADERS_FIELD, KEY_ID_FIELD, SIGNATURE_FIELD, }; #[derive(Debug)] pub struct Signed { signature: String, sig_headers: Vec, created: DateTime, expires: DateTime, key_id: String, } #[derive(Debug)] pub struct Unsigned { pub(crate) signing_string: String, pub(crate) sig_headers: Vec, pub(crate) created: DateTime, pub(crate) expires: DateTime, } impl Signed { pub fn signature_header(self) -> String { format!("Signature {}", self.into_header()) } pub fn authorization_header(self) -> String { self.into_header() } fn into_header(self) -> String { let header_parts = [ (KEY_ID_FIELD, self.key_id), (ALGORITHM_FIELD, ALGORITHM_VALUE.to_owned()), (CREATED_FIELD, self.created.timestamp().to_string()), (EXPIRES_FIELD, self.expires.timestamp().to_string()), (HEADERS_FIELD, self.sig_headers.join(" ")), (SIGNATURE_FIELD, self.signature), ]; header_parts .iter() .map(|(k, v)| format!("{}=\"{}\"", k, v)) .collect::>() .join(",") } } impl Unsigned { pub fn sign(self, key_id: String, f: F) -> Result where F: FnOnce(&str) -> Result, { (f)(&self.signing_string).map(|signature| Signed { signature, sig_headers: self.sig_headers, created: self.created, expires: self.expires, key_id, }) } }