use crate::gate::run_gate_logic;
use crate::jsonrpc::{Error, Result};
use crate::workspace_edit::apply_workspace_edit;
use crate::{lock_registry, sha256, update_diagnostics};
pub async fn max_explain_diagnostic(params: String) -> Result<max_protocol::MaxDiagnostic> {
let mut registry = lock_registry()?;
update_diagnostics(&mut registry);
if let Some(diag) = registry.diagnostics.get(¶ms) {
Ok(diag.clone())
} else {
Err(Error::invalid_params(format!(
"Diagnostic '{}' not found",
params
)))
}
}
pub async fn max_repair_plan(params: String) -> Result<Vec<max_protocol::MaxCodeAction>> {
let mut registry = lock_registry()?;
update_diagnostics(&mut registry);
if let Some(plans) = registry.repair_plans.get(¶ms) {
return Ok(plans.clone());
}
let mut matched = Vec::new();
for plans in registry.repair_plans.values() {
for plan in plans {
if let Some(ref diags) = plan.action.diagnostics {
for d in diags {
if let Some(max_d) = registry
.diagnostics
.values()
.find(|md| md.lsp.message == d.message)
{
if max_d.law_id == params {
matched.push(plan.clone());
}
}
}
}
}
}
if !matched.is_empty() {
return Ok(matched);
}
Err(Error::invalid_params(format!(
"No repair plan found for '{}'",
params
)))
}
pub async fn max_apply_repair_transaction(
params: max_protocol::MaxCodeAction,
) -> Result<max_protocol::Receipt> {
let (current_state, root_path, workspace_edit, gate_names, diag_filter) = {
let mut registry = lock_registry()?;
update_diagnostics(&mut registry);
for pre in ¶ms.preconditions {
if pre.condition == "State is Uninitialized"
&& registry.current_state != crate::service::State::Uninitialized
{
return Err(Error::invalid_params(format!(
"Precondition failed: Server state is {:?}, but condition requires State is Uninitialized.",
registry.current_state
)));
}
}
for expected in ¶ms.receipt_plan.expected_receipts {
if !registry.receipts.contains_key(expected) {
return Err(Error::invalid_params(format!(
"Receipt integrity violation: Required cryptographic receipt '{}' is not present in the registry.",
expected
)));
}
}
if params.action.edit.is_some() && params.validation_plan.gates.is_empty() {
return Err(Error::invalid_params(
"Unsafe transaction: A workspace edit is not called 'safe' unless there is an explicit validation plan (non-empty gates)."
));
}
let current_state = registry.current_state;
let root_path = registry.root_path.clone();
let workspace_edit = params.action.edit.clone();
let gate_names: Vec<String> = params
.validation_plan
.gates
.iter()
.map(|g| g.0.clone())
.collect();
let diag_filter: Option<Vec<(String, lsp_types_max::Range)>> = params
.action
.diagnostics
.as_ref()
.map(|diags| diags.iter().map(|d| (d.message.clone(), d.range)).collect());
(
current_state,
root_path,
workspace_edit,
gate_names,
diag_filter,
)
};
let mut backups = std::collections::HashMap::new();
if let Some(ref edit) = workspace_edit {
if let Some(ref changes) = edit.changes {
for url in changes.keys() {
if let Ok(parsed_url) = url::Url::parse(url.as_str()) {
if let Ok(path) = parsed_url.to_file_path() {
let content = if path.exists() {
std::fs::read_to_string(&path).ok()
} else {
None
};
backups.insert(path, content);
}
}
}
}
if let Err(e) = apply_workspace_edit(edit) {
return Err(Error::invalid_params(format!(
"Failed to apply edits: {}",
e
)));
}
}
let mut validation_failed = false;
let mut failed_gate = String::new();
for gate_name in &gate_names {
if !run_gate_logic(gate_name, current_state, root_path.clone()) {
validation_failed = true;
failed_gate = gate_name.clone();
break;
}
}
if validation_failed {
for (path, backup) in backups {
if let Some(old_content) = backup {
let _ = std::fs::write(&path, old_content);
} else {
let _ = std::fs::remove_file(&path);
}
}
return Err(Error::invalid_params(format!(
"Transaction validation failed: validation gate '{}' failed check. Rolled back changes.",
failed_gate
)));
}
let serialized = serde_json::to_vec(¶ms).map_err(|e| {
let _ = e;
Error::internal_error()
})?;
let hash = sha256(&serialized);
let receipt_id = if params.action.title.contains("security authorization") {
"rcpt-security-auth".to_string()
} else {
format!("rcpt-{}", &hash[0..16])
};
let receipt = max_protocol::Receipt {
receipt_id: receipt_id.clone(),
hash,
prev_receipt_hash: None,
};
{
let mut registry = lock_registry()?;
for gate_name in &gate_names {
registry.gates.insert(gate_name.clone(), true);
}
if let Some(ref filter) = diag_filter {
let mut resolved_ids = Vec::new();
for (msg, range) in filter {
for (id, max_d) in ®istry.diagnostics {
if &max_d.lsp.message == msg && &max_d.lsp.range == range {
resolved_ids.push(id.clone());
}
}
}
for id in resolved_ids {
registry.cleared_diagnostics.insert(id.clone());
registry.diagnostics.remove(&id);
registry.repair_plans.remove(&id);
}
}
update_diagnostics(&mut registry);
registry.receipts.insert(receipt_id, receipt.clone());
}
Ok(receipt)
}
pub async fn max_run_gate(params: max_protocol::GateId) -> Result<bool> {
let mut registry = lock_registry()?;
update_diagnostics(&mut registry);
let success = run_gate_logic(
¶ms.0,
registry.current_state,
registry.root_path.clone(),
);
registry.gates.insert(params.0.clone(), success);
Ok(success)
}