use std::sync::Arc;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize};
use crate::{Capability, ExecutionContext, FindingOrigin, Language};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub struct SourceLocation {
pub file: Arc<str>,
pub line: usize,
pub column: usize,
}
#[derive(Serialize, Clone, Debug, PartialEq, Eq, Hash)]
pub struct CapabilityFinding {
pub capability: Capability,
pub location: SourceLocation,
pub evidence: Arc<str>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub origin: Option<FindingOrigin>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub language: Option<Language>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub execution_context: Option<ExecutionContext>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reachable: Option<bool>,
}
impl<'de> Deserialize<'de> for CapabilityFinding {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct Raw {
capability: Capability,
location: SourceLocation,
evidence: Arc<str>,
#[serde(default)]
origin: Option<FindingOrigin>,
#[serde(default)]
language: Option<Language>,
#[serde(default)]
execution_context: Option<ExecutionContext>,
#[serde(default)]
build_script: Option<bool>,
#[serde(default)]
reachable: Option<bool>,
}
let raw = Raw::deserialize(deserializer)?;
let execution_context = match (raw.execution_context, raw.build_script) {
(Some(ExecutionContext::BuildHook), Some(true)) => Some(ExecutionContext::BuildHook),
(Some(ExecutionContext::BuildHook), Some(false)) => {
return Err(D::Error::custom(
"execution_context=build_hook contradicts legacy build_script=false",
));
}
(Some(ctx), Some(true)) => {
return Err(D::Error::custom(format!(
"execution_context={ctx:?} contradicts legacy build_script=true"
)));
}
(Some(ctx), Some(false) | None) => Some(ctx),
(None, Some(true)) => Some(ExecutionContext::BuildHook),
(None, Some(false) | None) => None,
};
Ok(Self {
capability: raw.capability,
location: raw.location,
evidence: raw.evidence,
origin: raw.origin,
language: raw.language,
execution_context,
reachable: raw.reachable,
})
}
}
impl CapabilityFinding {
pub fn is_build_hook(&self) -> bool {
self.execution_context == Some(ExecutionContext::BuildHook)
}
}