use crate::{ScopeKind, StatusCode, SubjectKind, UncertaintyCode, WarningCode};
use blazingly_json::Value;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub enum GraphRevision {
#[default]
Missing,
Null,
Value(String),
}
impl GraphRevision {
#[must_use]
pub const fn is_missing(&self) -> bool {
matches!(self, Self::Missing)
}
#[must_use]
pub fn as_deref(&self) -> Option<&str> {
match self {
Self::Value(value) => Some(value),
Self::Missing | Self::Null => None,
}
}
}
impl Serialize for GraphRevision {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Missing | Self::Null => serializer.serialize_none(),
Self::Value(value) => serializer.serialize_str(value),
}
}
}
impl<'de> Deserialize<'de> for GraphRevision {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Option::<String>::deserialize(deserializer)
.map(|value| value.map_or(Self::Null, Self::Value))
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TypedSubject {
pub kind: SubjectKind,
pub value: String,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
impl TypedSubject {
#[must_use]
pub fn new(kind: impl Into<SubjectKind>, value: impl Into<String>) -> Self {
Self {
kind: kind.into(),
value: value.into(),
extensions: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EvidenceScope {
pub kind: ScopeKind,
pub value: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub roots: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub languages: Vec<String>,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
impl EvidenceScope {
#[must_use]
pub fn new(kind: impl Into<ScopeKind>, value: impl Into<String>) -> Self {
Self {
kind: kind.into(),
value: value.into(),
roots: Vec::new(),
languages: Vec::new(),
extensions: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlannerIdentity {
pub name: String,
pub version: String,
pub backend: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backend_version: Option<String>,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
impl PlannerIdentity {
#[must_use]
pub fn new(
name: impl Into<String>,
version: impl Into<String>,
backend: impl Into<String>,
) -> Self {
Self {
name: name.into(),
version: version.into(),
backend: backend.into(),
backend_version: None,
extensions: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CompletenessProof {
pub scope: EvidenceScope,
pub planner: PlannerIdentity,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
impl CompletenessProof {
#[must_use]
pub fn new(scope: EvidenceScope, planner: PlannerIdentity) -> Self {
Self {
scope,
planner,
extensions: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UncertainReference {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub line: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subject: Option<TypedSubject>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<UncertaintyCode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<UncertaintyCode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub excerpt: Option<String>,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
impl UncertainReference {
#[must_use]
pub fn repo_path(&self) -> Option<&str> {
self.path.as_deref().or(self.file.as_deref())
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NotModified {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subject: Option<TypedSubject>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub operation_index: Option<u32>,
pub reason: String,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlanEvidence {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(default, skip_serializing_if = "GraphRevision::is_missing")]
pub graph_revision: GraphRevision,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completeness_proof: Option<CompletenessProof>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub uncertain_references: Option<Vec<UncertainReference>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub not_modified: Option<Vec<NotModified>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub warnings: Option<Vec<WarningCode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub follow_up: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub syntax_check: Option<StatusCode>,
#[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
pub extensions: BTreeMap<String, Value>,
}
pub type PlanAnnotations = PlanEvidence;