use anyhow::Result;
use pushkin_core::manifest::{ContractName, Manifest};
use pushkin_core::pipeline::{check_write, WriteRequest};
use std::collections::BTreeSet;
use super::{apply_waivers, load_manifest};
pub fn run(base: Option<&str>) -> Result<i32> {
let manifest = load_manifest()?;
let files = changed_files(base)?;
let touched: Vec<&String> = files
.iter()
.filter(|file| manifest.mapping_for(file).is_some())
.collect();
if touched.is_empty() {
println!("pushkin affected: no mapped files in the diff.");
return Ok(0);
}
let contracts: BTreeSet<&str> = touched
.iter()
.filter_map(|file| manifest.mapping_for(file))
.flat_map(|mapping| mapping.contracts.iter().map(ContractName::as_str))
.collect();
println!("pushkin affected: touched mapped files");
for file in &touched {
println!(" {file}");
}
for name in &contracts {
println!("contract: {name}");
blast_radius(&manifest, name);
}
let mut blocked = false;
for file in &touched {
let Ok(content) = std::fs::read_to_string(file) else {
continue;
};
let result = apply_waivers(check_write(
&manifest,
&WriteRequest {
file_path: (*file).clone(),
content,
},
));
for violation in &result.violations {
blocked = true;
println!(
" violation {}:{} [{}] — {}",
violation.file, violation.line, violation.rule, violation.fix_hint
);
}
}
Ok(if blocked { 2 } else { 0 })
}
fn blast_radius(manifest: &Manifest, contract_name: &str) {
let Some(contract) = manifest
.contracts
.iter()
.find(|contract| contract.name.as_str() == contract_name)
else {
return;
};
for emit in &contract.emit {
let file_name = match emit.as_str() {
"zod" => format!("{contract_name}.zod.gen.ts"),
"pydantic" => format!("{contract_name}_models.gen.py"),
"rust" => format!("{contract_name}.gen.rs"),
"sql" => format!("{contract_name}.gen.sql"),
other => other.to_owned(),
};
println!(" binding: generated/{file_name}");
}
for mapping in &manifest.mappings {
if mapping
.contracts
.iter()
.any(|name| name.as_str() == contract_name)
{
println!(" mapped: {}", mapping.glob);
}
}
}
fn changed_files(base: Option<&str>) -> Result<Vec<String>> {
match base {
Some(reference) => super::git::changed_in_range(reference),
None => super::git::staged_files(),
}
}