use crate::fix::FixStrategyEntry;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct KonveyorRuleset {
pub name: String,
pub description: String,
pub labels: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KonveyorRule {
#[serde(rename = "ruleID")]
pub rule_id: String,
pub labels: Vec<String>,
pub effort: u32,
pub category: String,
pub description: String,
pub message: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub links: Vec<KonveyorLink>,
pub when: KonveyorCondition,
#[serde(skip)]
pub fix_strategy: Option<FixStrategyEntry>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KonveyorLink {
pub url: String,
pub title: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum KonveyorCondition {
FileContent {
#[serde(rename = "builtin.filecontent")]
filecontent: FileContentFields,
},
Json {
#[serde(rename = "builtin.json")]
json: JsonFields,
},
FrontendReferenced {
#[serde(rename = "frontend.referenced")]
referenced: FrontendReferencedFields,
},
FrontendCssClass {
#[serde(rename = "frontend.cssclass")]
cssclass: FrontendPatternFields,
},
FrontendCssVar {
#[serde(rename = "frontend.cssvar")]
cssvar: FrontendPatternFields,
},
Or {
or: Vec<KonveyorCondition>,
},
And {
and: Vec<KonveyorCondition>,
},
FileContentNegated {
#[serde(rename = "not")]
negated: bool,
#[serde(rename = "builtin.filecontent")]
filecontent: FileContentFields,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FrontendPatternFields {
pub pattern: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FileContentFields {
pub pattern: String,
#[serde(rename = "filePattern")]
pub file_pattern: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct JsonFields {
pub xpath: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub filepaths: Option<Vec<String>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FrontendReferencedFields {
pub pattern: String,
pub location: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub component: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub parent: Option<String>,
#[serde(
rename = "parentFrom",
skip_serializing_if = "Option::is_none",
default
)]
pub parent_from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub from: Option<String>,
}
pub fn extract_frontend_refs(condition: &KonveyorCondition) -> Vec<&FrontendReferencedFields> {
match condition {
KonveyorCondition::FrontendReferenced { referenced } => vec![referenced],
KonveyorCondition::Or { or } => or.iter().flat_map(extract_frontend_refs).collect(),
KonveyorCondition::And { and } => and.iter().flat_map(extract_frontend_refs).collect(),
_ => vec![],
}
}
pub fn extract_file_pattern_from_condition(condition: &KonveyorCondition) -> Option<String> {
match condition {
KonveyorCondition::FileContent { filecontent } => Some(filecontent.file_pattern.clone()),
KonveyorCondition::Or { or } => or.first().and_then(extract_file_pattern_from_condition),
_ => None,
}
}
pub fn dedup_conditions(conditions: Vec<KonveyorCondition>) -> Vec<KonveyorCondition> {
use std::collections::BTreeSet;
let mut seen = BTreeSet::new();
let mut unique = Vec::new();
for cond in conditions {
let key = serde_json::to_string(&cond).unwrap_or_default();
if seen.insert(key) {
unique.push(cond);
}
}
unique
}