Skip to main content

unsafe_review_cli/
lib.rs

1#![forbid(unsafe_code)]
2mod command;
3mod execute;
4mod lsp;
5mod parse;
6
7/// A typed failure returned by [`run`].
8///
9/// The distinction lets callers map policy violations to exit 1 and all other
10/// failures (usage errors, I/O errors, internal errors) to exit 2, matching the
11/// stable exit-code contract:
12///
13/// ```text
14/// 0 = ran to completion: clean, or advisory findings
15/// 1 = ran to completion: no-new-debt policy found new/worsened coverage gaps
16/// 2 = tool did not complete a review: usage, input/IO, or internal error
17/// ```
18pub enum RunFailure {
19    /// The tool ran to completion and the no-new-debt policy found new or
20    /// worsened coverage gaps.
21    PolicyViolation(String),
22    /// The tool did not complete a review: usage error, missing/unreadable
23    /// input, I/O error, or internal error.
24    Tool(String),
25}
26
27impl std::fmt::Display for RunFailure {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            RunFailure::PolicyViolation(msg) | RunFailure::Tool(msg) => f.write_str(msg),
31        }
32    }
33}
34
35/// Converts a plain `String` error into the [`RunFailure::Tool`] variant so
36/// that the hundreds of existing `?`-propagated `String` error sites remain
37/// unchanged.
38impl From<String> for RunFailure {
39    fn from(msg: String) -> Self {
40        RunFailure::Tool(msg)
41    }
42}
43
44pub fn run(args: impl IntoIterator<Item = String>) -> Result<(), RunFailure> {
45    let command = parse::parse(args).map_err(RunFailure::Tool)?;
46    execute::execute(command)
47}
48
49#[cfg(test)]
50mod tests {
51    use super::RunFailure;
52
53    #[test]
54    fn string_converts_to_tool_variant() {
55        let f: RunFailure = "some tool error".to_string().into();
56        assert!(
57            matches!(f, RunFailure::Tool(_)),
58            "From<String> must map to RunFailure::Tool, not PolicyViolation"
59        );
60    }
61
62    #[test]
63    fn display_shows_inner_message() {
64        let tool = RunFailure::Tool("tool message".to_string());
65        assert_eq!(tool.to_string(), "tool message");
66        let policy = RunFailure::PolicyViolation("policy message".to_string());
67        assert_eq!(policy.to_string(), "policy message");
68    }
69}