use serde::{Deserialize, Serialize};
use crate::protocol::PLUGIN_PROTOCOL_VERSION;
use crate::trigger::GitEvent;
use crate::verdict::{Finding, Severity};
pub const KLASP_PLUGIN_BIN_PREFIX: &str = "klasp-plugin-";
pub const KLASP_PLUGIN_RULE: &str = "klasp::plugin";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginDescribe {
pub protocol_version: u32,
pub name: String,
pub config_types: Vec<String>,
#[serde(default)]
pub supports: PluginSupports,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PluginSupports {
#[serde(default)]
pub verdict_v0: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PluginTriggerKind {
Commit,
Push,
}
impl From<GitEvent> for PluginTriggerKind {
fn from(event: GitEvent) -> Self {
match event {
GitEvent::Commit => PluginTriggerKind::Commit,
GitEvent::Push => PluginTriggerKind::Push,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginTrigger {
pub kind: PluginTriggerKind,
pub files: Vec<String>,
}
impl PluginTrigger {
pub fn from_event(event: GitEvent, staged_files: &[std::path::PathBuf]) -> Self {
Self {
kind: event.into(),
files: staged_files
.iter()
.map(|p| p.to_string_lossy().into_owned())
.collect(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginGateInput {
pub protocol_version: u32,
pub schema_version: u32,
pub trigger: PluginTrigger,
pub config: PluginConfig,
pub repo_root: String,
pub base_ref: String,
}
impl PluginGateInput {
pub fn new(
trigger: PluginTrigger,
config: PluginConfig,
repo_root: &std::path::Path,
base_ref: &str,
) -> Self {
Self {
protocol_version: PLUGIN_PROTOCOL_VERSION,
schema_version: crate::protocol::GATE_SCHEMA_VERSION,
trigger,
config,
repo_root: repo_root.to_string_lossy().into_owned(),
base_ref: base_ref.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginConfig {
pub r#type: String,
#[serde(default)]
pub args: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub settings: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginGateOutput {
pub protocol_version: u32,
pub verdict: PluginVerdict,
#[serde(default)]
pub findings: Vec<PluginFinding>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PluginVerdict {
Pass,
Warn,
Fail,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginFinding {
pub severity: Severity,
pub rule: 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>,
pub message: String,
}
impl From<PluginFinding> for Finding {
fn from(pf: PluginFinding) -> Self {
Finding {
rule: pf.rule,
message: pf.message,
file: pf.file,
line: pf.line,
severity: pf.severity,
}
}
}
pub fn plugin_error_warn(plugin_name: &str, reason: impl Into<String>) -> crate::verdict::Verdict {
let reason = reason.into();
crate::verdict::Verdict::Warn {
findings: vec![Finding {
rule: KLASP_PLUGIN_RULE.to_string(),
message: format!("plugin `{plugin_name}`: {reason}"),
file: None,
line: None,
severity: Severity::Warn,
}],
message: None,
}
}