diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b1370c905..949617e79 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -29,9 +29,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: - # Fixed version for clippy lints. Bump this as necesary. It must not - # be older than the MSRV in tests.yml. - toolchain: "1.64" + toolchain: "1.64" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 @@ -100,8 +98,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: - # Same as the MSRV in rust-tests.yml - toolchain: "1.64" + toolchain: "1.64" # MSRV override: true - uses: actions-rs/cargo@v1.0.3 diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml index 776642ee6..440debee1 100644 --- a/.github/workflows/rust-tests.yml +++ b/.github/workflows/rust-tests.yml @@ -15,8 +15,7 @@ jobs: strategy: matrix: rust: - # MSRV; must not be higher than the clippy rust version in checks.yml - - "1.64" + - "1.64" # MSRV - "stable" os: - ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 5516ef5bf..cff3462b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2189,6 +2189,7 @@ name = "xtask" version = "0.4.1" dependencies = [ "anyhow", + "regex", "taskchampion-lib", ] diff --git a/src/tc/rust/Cargo.lock b/src/tc/rust/Cargo.lock index 2b64a4d80..7320a5524 100644 --- a/src/tc/rust/Cargo.lock +++ b/src/tc/rust/Cargo.lock @@ -60,9 +60,9 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" diff --git a/taskchampion/xtask/Cargo.toml b/taskchampion/xtask/Cargo.toml index 32f381977..9a4c71e59 100644 --- a/taskchampion/xtask/Cargo.toml +++ b/taskchampion/xtask/Cargo.toml @@ -2,8 +2,11 @@ name = "xtask" version = "0.4.1" edition = "2018" +# rust-version = "1.65" # Used for testing xtask MSRV functionality [dependencies] anyhow.workspace = true taskchampion-lib = { path = "../lib" } + +regex = "^1.5.6" diff --git a/taskchampion/xtask/src/main.rs b/taskchampion/xtask/src/main.rs index 47909b4c8..e7db2f21e 100644 --- a/taskchampion/xtask/src/main.rs +++ b/taskchampion/xtask/src/main.rs @@ -3,29 +3,121 @@ //! At the moment it is very simple, but if this grows more subcommands then //! it will be sensible to use `clap` or another similar library. +use regex::Regex; use std::env; use std::fs::File; -use std::io::Write; -use std::path::PathBuf; +use std::io::{BufRead, BufReader, Seek, Write}; +use std::path::{Path, PathBuf}; + +/// Tuples of the form (PATH, REGEX) where PATH and REGEX are literals where PATH is a file that +/// conains the Minimum Supported Rust Version and REGEX is the pattern to find the appropriate +/// line in the file. PATH is relative to the `taskchampion/` directory in the repo. +const MSRV_PATH_REGEX: &[(&str, &str)] = &[ + ( + "../.github/workflows/checks.yml", + r#"toolchain: "[0-9.]+*" # MSRV"#, + ), + ("../.github/workflows/rust-tests.yml", r#""[0-9.]+" # MSRV"#), + ( + "taskchampion/src/lib.rs", + r#"Rust version [0-9.]* and higher"#, + ), +]; pub fn main() -> anyhow::Result<()> { - let arg = env::args().nth(1); - match arg.as_deref() { - Some("codegen") => codegen(), - Some(arg) => anyhow::bail!("unknown xtask {}", arg), - _ => anyhow::bail!("unknown xtask"), + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); + let workspace_dir = manifest_dir.parent().unwrap(); + let arguments: Vec = env::args().collect(); + + if arguments.len() < 2 { + anyhow::bail!("xtask: Valid arguments are: `codegen`, `msrv `"); + } + + match arguments[1].as_str() { + "codegen" => codegen(workspace_dir), + "msrv" => msrv(arguments, workspace_dir), + _ => anyhow::bail!("xtask: unknown xtask"), } } /// `cargo xtask codegen` /// /// This uses ffizz-header to generate `lib/taskchampion.h`. -fn codegen() -> anyhow::Result<()> { - let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let workspace_dir = manifest_dir.parent().unwrap(); +fn codegen(workspace_dir: &Path) -> anyhow::Result<()> { let lib_crate_dir = workspace_dir.join("lib"); let mut file = File::create(lib_crate_dir.join("taskchampion.h")).unwrap(); write!(&mut file, "{}", ::taskchampion_lib::generate_header()).unwrap(); Ok(()) } + +/// `cargo xtask msrv (X.Y)` +/// +/// This checks and updates the Minimum Supported Rust Version for all files specified in MSRV_PATH_REGEX`. +/// Each line where the regex matches will have all values of the form `#.##` replaced with the given MSRV. +fn msrv(args: Vec, workspace_dir: &Path) -> anyhow::Result<()> { + // check that (X.Y) argument is (mostly) valid: + if args.len() < 3 || !args[2].chars().all(|c| c.is_numeric() || c == '.') { + anyhow::bail!("xtask: Invalid argument format. Xtask MSRV argument takes the form \"X.Y(y)\", where XYy are numbers. eg: `cargo run xtask MSRV 1.68`"); + } + let version_replacement_string = &args[2]; + + // set regex for replacing version number only within the pattern found within a line + let re_msrv_version = Regex::new(r"([0-9]+(\.|[0-9]+|))+")?; + + // for each file in const paths tuple + for msrv_file in MSRV_PATH_REGEX { + let mut is_pattern_in_file = false; + + let path = workspace_dir.join(msrv_file.0); + let path = Path::new(&path); + if !path.exists() { + anyhow::bail!("xtask: path does not exist {}", &path.display()); + }; + + let mut file = File::options().read(true).write(true).open(path)?; + let reader = BufReader::new(&file); + + // set search string and the replacement string for version number content + let re_msrv_pattern = Regex::new(msrv_file.1)?; + + // for each line in file + let mut file_string = String::new(); + for line in reader.lines() { + let line = &line?; + + // if rust version pattern is found and is different, update it + if let Some(pattern_offset) = re_msrv_pattern.find(line) { + if !pattern_offset.as_str().contains(version_replacement_string) { + file_string += &re_msrv_version.replace(line, version_replacement_string); + + file_string += "\n"; + + is_pattern_in_file = true; + continue; + } + } + + file_string += line; + file_string += "\n"; + } + + // if pattern was found and updated, write to disk + if is_pattern_in_file { + // Set the file length to the file_string length + file.set_len(file_string.len() as u64)?; + + // set the cursor to the beginning of the file and write + file.seek(std::io::SeekFrom::Start(0))?; + file.write_all(file_string.as_bytes())?; + + // notify user this file was updated + println!( + "xtask: Updated MSRV in {}", + re_msrv_version.replace(msrv_file.0, version_replacement_string) + ); + } + } + + Ok(()) +}