use language_tags::LanguageTag;
use crate::component::Component;
use crate::error::{CpeError, Result};
use crate::uri::*;
use crate::wfn::*;
use std::convert::TryFrom;
use std::str::FromStr;
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub enum Language {
#[default]
Any,
Language(LanguageTag),
}
impl FromStr for Language {
type Err = CpeError;
fn from_str(s: &str) -> Result<Self> {
if s == "ANY" {
Ok(Self::Any)
} else {
Ok(Self::Language(s.parse()?))
}
}
}
use std::fmt;
impl fmt::Display for Language {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Any => {
if f.alternate() {
write!(f, "*")
} else {
write!(f, "ANY")
}
}
Self::Language(tag) => write!(f, "{}", tag),
}
}
}
pub trait Cpe {
fn part(&self) -> CpeType;
fn vendor(&self) -> Component;
fn product(&self) -> Component;
fn version(&self) -> Component;
fn update(&self) -> Component;
fn edition(&self) -> Component;
fn language(&self) -> &Language;
fn sw_edition(&self) -> Component;
fn target_sw(&self) -> Component;
fn target_hw(&self) -> Component;
fn other(&self) -> Component;
}
macro_rules! impl_cpe {
($t:ty) => {
impl Cpe for $t {
fn part(&self) -> CpeType {
self.part
}
fn vendor(&self) -> Component {
self.vendor.as_component()
}
fn product(&self) -> Component {
self.product.as_component()
}
fn version(&self) -> Component {
self.version.as_component()
}
fn update(&self) -> Component {
self.update.as_component()
}
fn edition(&self) -> Component {
self.edition.as_component()
}
fn language(&self) -> &Language {
&self.language
}
fn sw_edition(&self) -> Component {
self.sw_edition.as_component()
}
fn target_sw(&self) -> Component {
self.target_sw.as_component()
}
fn target_hw(&self) -> Component {
self.target_hw.as_component()
}
fn other(&self) -> Component {
self.other.as_component()
}
}
};
($t:ty, $_l:lifetime) => {
impl Cpe for $t {
fn part(&self) -> CpeType {
self.part
}
fn vendor(&self) -> Component {
self.vendor.clone()
}
fn product(&self) -> Component {
self.product.clone()
}
fn version(&self) -> Component {
self.version.clone()
}
fn update(&self) -> Component {
self.update.clone()
}
fn edition(&self) -> Component {
self.edition.clone()
}
fn language(&self) -> &Language {
&self.language
}
fn sw_edition(&self) -> Component {
self.sw_edition.clone()
}
fn target_sw(&self) -> Component {
self.target_sw.clone()
}
fn target_hw(&self) -> Component {
self.target_hw.clone()
}
fn other(&self) -> Component {
self.other.clone()
}
}
};
}
impl_cpe!(OwnedUri);
impl_cpe!(OwnedWfn);
impl_cpe!(Uri<'_>, 'a);
impl_cpe!(Wfn<'_>, 'a);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CpeType {
Any,
Hardware,
OperatingSystem,
Application,
Empty,
}
impl Default for CpeType {
fn default() -> Self {
Self::Any
}
}
impl TryFrom<&str> for CpeType {
type Error = CpeError;
fn try_from(val: &str) -> Result<Self> {
Ok(match val {
"ANY" => Self::Any,
"h" => Self::Hardware,
"o" => Self::OperatingSystem,
"a" => Self::Application,
"" => Self::Empty,
_ => {
return Err(CpeError::InvalidCpeType {
value: val.to_owned(),
})
}
})
}
}
impl fmt::Display for CpeType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Any => {
if f.alternate() {
write!(f, "*")
} else {
write!(f, "ANY")
}
}
Self::Hardware => write!(f, "h"),
Self::OperatingSystem => write!(f, "o"),
Self::Application => write!(f, "a"),
Self::Empty => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_missing_type() {
assert!(CpeType::try_from("").is_ok());
}
#[test]
fn test_invalid_type() {
assert!(CpeType::try_from("x").is_err());
}
}