use std::{fmt::Display, str::FromStr};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use url::Url;
use crate::definitions::{AcknowledgmentsT, LangT, NotesT, ReferencesT, VersionT};
#[serde_as]
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Document {
#[serde_as(as = "DisplayFromStr")]
pub category: Category,
pub publisher: Publisher,
pub title: String,
pub tracking: Tracking,
pub csaf_version: CsafVersion,
pub acknowledgments: Option<AcknowledgmentsT>,
pub aggregate_severity: Option<AggregateSeverity>,
pub distribution: Option<Distribution>,
pub lang: Option<LangT>,
pub notes: Option<NotesT>,
pub references: Option<ReferencesT>,
pub source_lang: Option<LangT>,
}
#[derive(Debug)]
pub enum Category {
Base,
SecurityAdvisory,
Vex,
Other(String),
}
impl FromStr for Category {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"csaf_base" => Self::Base,
"csaf_security_advisory" => Self::SecurityAdvisory,
"csaf_vex" => Self::Vex,
_ => Self::Other(s.to_owned()),
})
}
}
impl Display for Category {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Base => write!(f, "csaf_base"),
Self::SecurityAdvisory => write!(f, "csaf_security_advisory"),
Self::Vex => write!(f, "csaf_vex"),
Self::Other(s) => write!(f, "{}", s),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub enum CsafVersion {
#[serde(rename = "2.0")]
TwoDotZero,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Publisher {
pub category: PublisherCategory,
pub name: String,
pub namespace: Url,
pub contact_details: Option<String>,
pub issuing_authority: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum PublisherCategory {
Coordinator,
Discoverer,
Other,
Translator,
User,
Vendor,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Tracking {
pub current_release_date: DateTime<Utc>,
pub id: String,
pub initial_release_date: DateTime<Utc>,
pub revision_history: Vec<Revision>,
pub status: Status,
pub version: VersionT,
pub aliases: Option<Vec<String>>,
pub generator: Option<Generator>,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Generator {
pub engine: Engine,
pub date: Option<DateTime<Utc>>,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Engine {
pub name: String,
pub version: Option<String>,
}
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
impl std::default::Default for Generator {
fn default() -> Self {
Self {
engine: Engine {
name: "csaf-rs".to_string(),
version: Some(CARGO_PKG_VERSION.to_string()),
},
date: Some(Utc::now()),
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Revision {
pub date: DateTime<Utc>,
pub legacy_version: Option<String>,
pub number: VersionT,
pub summary: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum Status {
Draft,
Final,
Interim,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct AggregateSeverity {
pub text: String,
pub namespace: Option<Url>,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Distribution {
pub text: Option<String>,
pub tlp: Option<Tlp>,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
pub struct Tlp {
pub label: TlpLabel,
pub url: Option<Url>,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum TlpLabel {
AMBER,
GREEN,
RED,
WHITE,
}