Repository: bitcoin/bitcoin
File: test/lint/test_runner/src/main.rs (Rust)

Code before:
        .success()
    {
        Ok(())
    } else {
        Err("".to_string())
    }
}

fn lint_all() -> LintResult {
    let mut good = true;
    let lint_dir = get_git_root().join("test/lint");
    for entry in fs::read_dir(lint_dir).unwrap() {
        let entry = entry.unwrap();
        let entry_fn = entry.file_name().into_string().unwrap();
        if entry_fn.starts_with("lint-")
            && entry_fn.ends_with(".py")
            && !Command::new("python3")
                .arg(entry.path())
                .status()
                .expect("command error")
                .success()
        {
            good = false;
            println!("^---- failure generated from {}", entry_fn);
        }
    }
    if good {
        Ok(())
    } else {
        Err("".to_string())
    }
}

fn main() -> ExitCode {
    let test_list: Vec<(&str, LintFn)> = vec![
        ("subtree check", lint_subtree),
        ("std::filesystem check", lint_std_filesystem),
        ("trailing whitespace check", lint_trailing_whitespace),
        ("no-tabs check", lint_tabs_whitespace),
        ("build config includes check", lint_includes_build_config),
        ("-help=1 documentation check", lint_doc),
        ("lint-*.py scripts", lint_all),
    ];

    let git_root = get_git_root();

    let mut test_failed = false;
    for (lint_name, lint_fn) in test_list {
        // chdir to root before each lint test
        env::set_current_dir(&git_root).unwrap();
        if let Err(err) = lint_fn() {

Review comment (suggestion):
Is the conversion to string objects needed? According to the docs, `join` will make a String by itself, no? https://doc.rust-lang.org/std/slice/trait.Join.html#associatedtype.Output-1

Code after:
        .success()
    {
        Ok(())
    } else {
        Err("".to_string())
    }
}

fn lint_markdown() -> LintResult {
    let bin_name = "mlc";
    let mut md_ignore_paths = get_subtrees();
    md_ignore_paths.push("./doc/README_doxygen.md");
    let md_ignore_path_str = md_ignore_paths.join(",");

    let mut cmd = Command::new(bin_name);
    cmd.args([
        "--offline",
        "--ignore-path",
        md_ignore_path_str.as_str(),
        "--root-dir",
        ".",
    ])
    .stdout(Stdio::null()); // Suppress overly-verbose output

    match cmd.output() {
        Ok(output) if output.status.success() => Ok(()),
        Ok(output) => {
            let stderr = String::from_utf8_lossy(&output.stderr);
            let filtered_stderr: String = stderr // Filter out this annoying trailing line
                .lines()
                .filter(|&line| line != "The following links could not be resolved:")
                .collect::<Vec<&str>>()
                .join("\n");
            Err(format!(
                r#"
One or more markdown links are broken.

Relative links are preferred (but not required) as jumping to file works natively within Emacs.

Markdown link errors found:
{}
                "#,
                filtered_stderr
            ))
        }
        Err(e) if e.kind() == ErrorKind::NotFound => {
            println!("`mlc` was not found in $PATH, skipping markdown lint check.");
            Ok(())
        }
        Err(e) => Err(format!("Error running mlc: {}", e)), // Misc errors
    }