use std::fmt;
use std::path::{Path, PathBuf};
use crate::credential::Credential;
use crate::error::{Error, Result};
use crate::jobs::{
Add, AttachSignature, ExtractData, ExtractSignature, RemoveSignature, Sign, Verify,
add_from_signed, attach_from, extract_data_from, extract_signature_from, remove_from,
sign_from_unsigned,
};
use crate::native::require_readable;
macro_rules! path_handle {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct $name {
path: PathBuf,
}
impl $name {
pub fn open(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref().to_path_buf();
require_readable(&path, "input")?;
Ok(Self { path })
}
pub(crate) fn from_path_unchecked(path: PathBuf) -> Self {
Self { path }
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
#[must_use]
pub fn into_path(self) -> PathBuf {
self.path
}
}
impl AsRef<Path> for $name {
fn as_ref(&self) -> &Path {
self.path()
}
}
impl fmt::Display for $name {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.path.display())
}
}
impl TryFrom<&Path> for $name {
type Error = Error;
fn try_from(path: &Path) -> Result<Self> {
Self::open(path)
}
}
impl TryFrom<PathBuf> for $name {
type Error = Error;
fn try_from(path: PathBuf) -> Result<Self> {
Self::open(path)
}
}
impl From<$name> for PathBuf {
fn from(value: $name) -> Self {
value.path
}
}
};
}
path_handle! {
Unsigned
}
impl Unsigned {
pub fn sign(self, credential: Credential) -> Sign<crate::jobs::NeedsOutput> {
sign_from_unsigned(self.path, credential)
}
pub fn attach_signature(
self,
signature: impl AsRef<Path>,
) -> AttachSignature<crate::jobs::NeedsOutput> {
attach_from(self.path, signature.as_ref().to_path_buf())
}
}
path_handle! {
Signed
}
impl Signed {
pub fn verify(&self) -> Verify {
Verify::new(&self.path)
}
pub fn add_timestamp(&self) -> Add<crate::jobs::NeedsOutput> {
add_from_signed(self.path.clone())
}
pub fn extract_signature(&self) -> ExtractSignature {
extract_signature_from(self.path.clone())
}
pub fn inspect(&self) -> Result<crate::SignatureInfo> {
use std::io::Read;
let mut der = Vec::new();
self.extract_signature()
.reader()?
.read_to_end(&mut der)
.map_err(|source| Error::Io {
field: "signature",
path: self.path.clone(),
source,
})?;
crate::inspect::from_der(&der)
}
pub fn extract_data(&self) -> ExtractData {
extract_data_from(self.path.clone())
}
pub fn strip(&self) -> RemoveSignature<crate::jobs::NeedsOutput> {
remove_from(self.path.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::credential::{Credential, Secret};
use crate::digest::Digest;
#[test]
fn typed_sign_builder_reaches_ready_without_argv() {
let _ready = Unsigned::from_path_unchecked("app.exe".into())
.sign(Credential::pkcs12("publisher.p12", Secret::value("x")))
.digest(Digest::Sha256)
.output("app-signed.exe");
}
}