tovuk 0.1.66

Deploy Rust workers, static frontends, and worker-static apps to Tovuk.
use super::report::DoctorCheck;
use crate::cli::constants::RUST_STRICT_CLIPPY_DENY_LINTS;
use std::{fs, path::Path};

pub(super) fn cargo_lints(project_dir: &Path) -> DoctorCheck {
    let cargo_toml = project_dir.join("Cargo.toml");
    let source = match fs::read_to_string(&cargo_toml) {
        Ok(source) => source,
        Err(error) => {
            return DoctorCheck {
                name: "cargo lints".to_owned(),
                ok: false,
                message: error.to_string(),
                agent_instruction: Some(
                    "Create Cargo.toml with strict Rust lints, then retry.".to_owned(),
                ),
            };
        }
    };
    let required_clippy_lints = RUST_STRICT_CLIPPY_DENY_LINTS
        .iter()
        .map(|lint| lint.trim_start_matches("clippy::"))
        .collect::<Vec<_>>();
    let cargo_toml = match source.parse::<toml::Table>() {
        Ok(cargo_toml) => cargo_toml,
        Err(error) => {
            return DoctorCheck {
                name: "cargo lints".to_owned(),
                ok: false,
                message: error.to_string(),
                agent_instruction: Some(
                    "Fix Cargo.toml syntax, then add strict Rust lints and retry.".to_owned(),
                ),
            };
        }
    };
    let ok = cargo_lint_level(&cargo_toml, "rust", "unsafe_code") == Some("forbid")
        && cargo_lint_level(&cargo_toml, "rust", "warnings") == Some("deny")
        && required_clippy_lints
            .iter()
            .all(|lint| cargo_lint_level(&cargo_toml, "clippy", lint) == Some("deny"));
    DoctorCheck {
        name: "cargo lints".to_owned(),
        ok,
        message: if ok {
            "strict".to_owned()
        } else {
            "missing strict Rust or Clippy resource lints".to_owned()
        },
        agent_instruction: if ok {
            None
        } else {
            Some("Add `[lints.rust]` with `unsafe_code = \"forbid\"` and `warnings = \"deny\"`, plus `[lints.clippy]` deny entries for all, pedantic, panic/unwrap bans, and resource lints, then retry.".to_owned())
        },
    }
}

fn cargo_lint_level<'a>(
    cargo_toml: &'a toml::Table,
    lint_group: &str,
    lint_name: &str,
) -> Option<&'a str> {
    lint_group_table(cargo_toml, &["lints", lint_group])
        .or_else(|| lint_group_table(cargo_toml, &["workspace", "lints", lint_group]))
        .and_then(|table| table.get(lint_name))
        .and_then(lint_assignment_level)
}

fn lint_group_table<'a>(cargo_toml: &'a toml::Table, path: &[&str]) -> Option<&'a toml::Table> {
    let mut table = cargo_toml;
    for segment in path {
        table = table.get(*segment)?.as_table()?;
    }
    Some(table)
}

fn lint_assignment_level(value: &toml::Value) -> Option<&str> {
    if let Some(level) = value.as_str() {
        return Some(level);
    }
    value
        .as_table()
        .and_then(|table| table.get("level"))
        .and_then(toml::Value::as_str)
}

#[cfg(test)]
mod tests {
    use super::{cargo_lint_level, lint_assignment_level};

    fn parse_cargo_toml(source: &str) -> toml::Table {
        match source.parse::<toml::Table>() {
            Ok(table) => table,
            Err(error) => {
                let message = error.to_string();
                assert!(message.is_empty(), "{message}");
                toml::Table::new()
            }
        }
    }

    #[test]
    fn cargo_lint_level_reads_string_and_inline_table_levels() {
        let cargo_toml = parse_cargo_toml(
            r#"
[lints.rust]
unsafe_code = "forbid"
warnings = "deny"

[lints.clippy]
unwrap_used = { level = "deny", priority = -1 }
"#,
        );

        assert_eq!(
            cargo_lint_level(&cargo_toml, "rust", "unsafe_code"),
            Some("forbid")
        );
        assert_eq!(
            cargo_lint_level(&cargo_toml, "clippy", "unwrap_used"),
            Some("deny")
        );
    }

    #[test]
    fn cargo_lint_level_falls_back_to_workspace_lints() {
        let cargo_toml = parse_cargo_toml(
            r#"
[workspace.lints.clippy]
panic = "deny"
"#,
        );

        assert_eq!(
            cargo_lint_level(&cargo_toml, "clippy", "panic"),
            Some("deny")
        );
    }

    #[test]
    fn lint_assignment_level_rejects_unstructured_values() {
        assert_eq!(lint_assignment_level(&toml::Value::Boolean(true)), None);
    }
}