use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum NotarizeStatus {
Pending,
InProgress,
Accepted,
Rejected(String),
Invalid(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotarizeResult {
pub status: NotarizeStatus,
pub submission_id: Option<String>,
pub log_entries: Vec<String>,
}
impl NotarizeResult {
pub fn is_success(&self) -> bool {
self.status == NotarizeStatus::Accepted
}
pub fn warnings(&self) -> Vec<&str> {
self.log_entries
.iter()
.filter(|line| line.to_lowercase().contains("warning"))
.map(|s| s.as_str())
.collect()
}
pub fn errors(&self) -> Vec<&str> {
self.log_entries
.iter()
.filter(|line| line.to_lowercase().contains("error"))
.map(|s| s.as_str())
.collect()
}
pub fn parse_log(&self) -> (Vec<&str>, Vec<&str>) {
(self.warnings(), self.errors())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_success_accepted() {
let result = NotarizeResult {
status: NotarizeStatus::Accepted,
submission_id: Some("sub-123".to_string()),
log_entries: vec![],
};
assert!(result.is_success());
}
#[test]
fn is_success_rejected() {
let result = NotarizeResult {
status: NotarizeStatus::Rejected("hardened runtime missing".to_string()),
submission_id: Some("sub-456".to_string()),
log_entries: vec![],
};
assert!(!result.is_success());
}
#[test]
fn is_success_pending() {
let result = NotarizeResult {
status: NotarizeStatus::Pending,
submission_id: None,
log_entries: vec![],
};
assert!(!result.is_success());
}
#[test]
fn parse_log_extracts_warnings_and_errors() {
let result = NotarizeResult {
status: NotarizeStatus::Accepted,
submission_id: None,
log_entries: vec![
"INFO: Processing started".to_string(),
"WARNING: unsigned framework detected".to_string(),
"ERROR: missing entitlement".to_string(),
"INFO: Completed".to_string(),
"Warning: deprecated API usage".to_string(),
],
};
let (warnings, errors) = result.parse_log();
assert_eq!(warnings.len(), 2);
assert_eq!(errors.len(), 1);
}
#[test]
fn parse_log_empty() {
let result = NotarizeResult {
status: NotarizeStatus::InProgress,
submission_id: None,
log_entries: vec![],
};
let (warnings, errors) = result.parse_log();
assert!(warnings.is_empty());
assert!(errors.is_empty());
}
#[test]
fn notarize_status_serialization() {
let statuses = vec![
NotarizeStatus::Pending,
NotarizeStatus::InProgress,
NotarizeStatus::Accepted,
NotarizeStatus::Rejected("reason".to_string()),
NotarizeStatus::Invalid("bad input".to_string()),
];
for status in statuses {
let json = serde_json::to_string(&status).unwrap();
let restored: NotarizeStatus = serde_json::from_str(&json).unwrap();
assert_eq!(restored, status);
}
}
#[test]
fn notarize_result_roundtrip() {
let result = NotarizeResult {
status: NotarizeStatus::Accepted,
submission_id: Some("sub-789".to_string()),
log_entries: vec!["INFO: done".to_string()],
};
let json = serde_json::to_string(&result).unwrap();
let restored: NotarizeResult = serde_json::from_str(&json).unwrap();
assert!(restored.is_success());
assert_eq!(restored.submission_id, Some("sub-789".to_string()));
}
}