1 // Copyright 2024 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use std::ffi::OsStr; 16 17 use xshell::cmd; 18 19 #[derive(clap::Subcommand, Debug, Clone)] 20 pub enum CargoWorkspaceSubcommand { 21 /// Checks test, clippy, and cargo deny in the workspace 22 CheckWorkspace(CargoOptions), 23 /// Checks the formatting of the workspace 24 CheckFormat(FormatterOptions), 25 } 26 27 impl CargoWorkspaceSubcommand { run(&self, tag: &str, sh: &xshell::Shell) -> anyhow::Result<()>28 pub fn run(&self, tag: &str, sh: &xshell::Shell) -> anyhow::Result<()> { 29 match self { 30 CargoWorkspaceSubcommand::CheckWorkspace(cargo_options) => { 31 cargo_options.check_workspace(sh, tag) 32 } 33 CargoWorkspaceSubcommand::CheckFormat(formatter_options) => { 34 formatter_options.check_format(sh) 35 } 36 } 37 } 38 } 39 40 #[derive(clap::Args, Debug, Clone, Default)] 41 pub struct CargoOptions { 42 #[arg(long, help = "whether to run cargo with --locked")] 43 locked: bool, 44 #[arg(long, help = "gather coverage metrics")] 45 coverage: bool, 46 } 47 48 impl CargoOptions { 49 /// Run `cargo test` or `cargo llvm-cov` depending on the configured options. test<'sh, S: AsRef<OsStr>>( &self, sh: &'sh xshell::Shell, tag: &str, args: impl IntoIterator<Item = S>, ) -> xshell::Cmd<'sh>50 pub fn test<'sh, S: AsRef<OsStr>>( 51 &self, 52 sh: &'sh xshell::Shell, 53 tag: &str, 54 args: impl IntoIterator<Item = S>, 55 ) -> xshell::Cmd<'sh> { 56 let locked = if self.locked { "--locked" } else { "" }; 57 if self.coverage { 58 cmd!( 59 sh, 60 "cargo llvm-cov {locked} {args...} --lcov --output-path target/{tag}.info -- --color=always" 61 ) 62 } else { 63 cmd!(sh, "cargo test {locked} {args...} -- --color=always") 64 } 65 } 66 67 /// Run the default set of checks on a cargo workspace check_workspace(&self, sh: &xshell::Shell, tag: &str) -> anyhow::Result<()>68 pub fn check_workspace(&self, sh: &xshell::Shell, tag: &str) -> anyhow::Result<()> { 69 self.test(sh, tag, ["--workspace"]).run()?; 70 cmd!( 71 sh, 72 "cargo clippy --all-targets --workspace -- --deny warnings" 73 ) 74 .run()?; 75 cmd!(sh, "cargo deny --workspace check").run()?; 76 // ensure the docs are valid (cross-references to other code, etc) 77 cmd!( 78 sh, 79 "cargo doc --quiet --workspace --no-deps --document-private-items 80 --target-dir target/dist_docs/{tag}" 81 ) 82 .env("RUSTDOCFLAGS", "--deny warnings") 83 .run()?; 84 Ok(()) 85 } 86 } 87 88 #[derive(clap::Args, Debug, Clone, Default)] 89 pub struct FormatterOptions { 90 #[arg( 91 long, 92 help = "reformat files files in the workspace with the code formatter" 93 )] 94 pub reformat: bool, 95 } 96 97 impl FormatterOptions { check_format(&self, sh: &xshell::Shell) -> anyhow::Result<()>98 pub fn check_format(&self, sh: &xshell::Shell) -> anyhow::Result<()> { 99 if self.reformat { 100 cmd!(sh, "cargo fmt").run()?; 101 } else { 102 cmd!(sh, "cargo fmt --check").run()?; 103 } 104 Ok(()) 105 } 106 } 107