Add explicit read-only mixed source8 CLI checking

This commit is contained in:
2026-09-24 21:57:06 -07:00
parent bab6f96a5b
commit f83f3bf45b
5 changed files with 663 additions and 30 deletions
+66 -11
View File
@@ -170,6 +170,47 @@ fn disk_error(report: &mut Report, error: SourceFileError) {
report.fail(3, code, error.to_string());
}
/// Validate captured mixed source8 bytes without creating an editable candidate.
pub fn check_mixed(entry: &Path) -> Report {
let mut report = Report::empty(entry, "check-mixed");
let snapshot = match source_files::load_mixed_source(entry, LIMITS) {
Ok(snapshot) => snapshot,
Err(error) => {
disk_error(&mut report, error);
return report;
}
};
let diagnostics = if snapshot.diagnostics().is_empty() {
match language::check_mixed_bundle(snapshot.bundle(), LIMITS) {
Ok(checked) => checked.warnings().to_vec(),
Err(errors) => errors,
}
} else {
snapshot.diagnostics().to_vec()
};
if diagnostics
.iter()
.any(|d| d.diagnostic.severity == Severity::Error)
{
report.status = "invalid".into();
report.exit_code = if diagnostics.iter().any(|d| {
matches!(
d.diagnostic.code.as_str(),
"pending_save" | "missing_include" | "invalid_path"
)
}) {
3
} else {
1
};
}
report.diagnostics = diagnostics
.into_iter()
.map(|d| located(d, "input"))
.collect();
report
}
/// A candidate owns its original expectations and is consumed by saving.
/// Callers cannot replace its source/model/expectations after preparation.
pub struct Prepared {
@@ -406,14 +447,19 @@ fn unified_diff(path: &str, before: &str, after: &str) -> String {
out
}
/// Read-only mixed checking cannot acquire ordinary edit/write authority.
pub enum Dispatch {
Editable { operation: Operation, write: bool },
CheckMixed,
}
pub struct Arguments {
pub entry: PathBuf,
pub operation: Operation,
pub write: bool,
pub dispatch: Dispatch,
pub json: bool,
}
pub const HELP: &str = "arcadia check ROOT [--json]\narcadia lint ROOT [--json]\narcadia stabilize ROOT [--write] [--json]\narcadia rename-alias ROOT OLD NEW [--write] [--json]\n\nROOT is an explicit .arcadia entry file; includes resolve relative to it.\nMutation commands preview by default. --write applies this invocation's candidate.\nMissing UUIDs in a preview are provisional until written. No canonical formatting.\nExit: 0 valid/success (engineering warnings allowed), 1 invalid source or semantic\ncommand rejection, 2 usage, 3 disk/path/conflict/pending-journal/output failure.\nNo implicit journal recovery. Limits: 64 MiB, 256 files, include depth32/edges1024,\n100000 graph objects, 250000 records, 500000 ordered references.\n";
pub const HELP: &str = "arcadia check ROOT [--json]\narcadia lint ROOT [--json]\narcadia check-mixed ROOT [--json]\narcadia stabilize ROOT [--write] [--json]\narcadia rename-alias ROOT OLD NEW [--write] [--json]\n\nROOT is an explicit .arcadia entry file; includes resolve relative to it.\ncheck-mixed requires source8 and explicit UUIDs; it never migrates, stabilizes or writes.\nMutation commands preview by default. --write applies this invocation's candidate.\nMissing UUIDs in a preview are provisional until written. No canonical formatting.\nExit: 0 valid/success (engineering warnings allowed), 1 invalid source or semantic\ncommand rejection, 2 usage, 3 disk/path/conflict/pending-journal/output failure.\nNo implicit journal recovery. Limits: 64 MiB, 256 files, include depth32/edges1024,\n100000 graph objects, 250000 records, 500000 ordered references.\n";
pub fn arguments(args: &[std::ffi::OsString]) -> Result<Arguments, String> {
let mut positional = Vec::new();
@@ -433,19 +479,28 @@ pub fn arguments(args: &[std::ffi::OsString]) -> Result<Arguments, String> {
}
}
let command = positional.first().and_then(|s| s.to_str()).unwrap_or("");
let operation = match (command, positional.len()) {
("check" | "lint", 2) if !write => Operation::Check,
("stabilize", 2) => Operation::Stabilize,
("rename-alias", 4) => Operation::RenameAlias {
old: positional[2].to_str().ok_or("Alias must be UTF-8")?.into(),
new: positional[3].to_str().ok_or("Alias must be UTF-8")?.into(),
let dispatch = match (command, positional.len()) {
("check-mixed", 2) if !write => Dispatch::CheckMixed,
("check" | "lint", 2) if !write => Dispatch::Editable {
operation: Operation::Check,
write,
},
("stabilize", 2) => Dispatch::Editable {
operation: Operation::Stabilize,
write,
},
("rename-alias", 4) => Dispatch::Editable {
operation: Operation::RenameAlias {
old: positional[2].to_str().ok_or("Alias must be UTF-8")?.into(),
new: positional[3].to_str().ok_or("Alias must be UTF-8")?.into(),
},
write,
},
_ => return Err(HELP.into()),
};
Ok(Arguments {
entry: PathBuf::from(positional[1]),
operation,
write,
dispatch,
json,
})
}