diff --git a/.gitignore b/.gitignore index ad67955..978c522 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ target # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +.trait-winnower.toml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 39b6119..da40e5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,12 +83,33 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "assert_fs" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652f6cb1f516886fcfee5e7a5c078b9ade62cfcb889524efe5a64d682dd27a9" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + [[package]] name = "bstr" version = "1.12.0" @@ -100,6 +121,12 @@ dependencies = [ "serde", ] +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + [[package]] name = "clap" version = "4.5.46" @@ -146,6 +173,31 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "difflib" version = "0.4.0" @@ -158,6 +210,28 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "float-cmp" version = "0.10.0" @@ -167,12 +241,80 @@ dependencies = [ "num-traits", ] +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92119844f513ffa41556430369ab02c295a3578af21cf945caa3e9e0c2481ac3" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -185,6 +327,18 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + [[package]] name = "memchr" version = "2.7.5" @@ -206,6 +360,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "once_cell_polyfill" version = "1.70.1" @@ -260,6 +420,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "regex" version = "1.11.2" @@ -289,26 +455,67 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" -version = "1.0.219" +version = "1.0.225" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.225" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_spanned" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2789234a13a53fc4be1b51ea1bab45a3c338bdb884862a257d10e5a74ae009e6" +dependencies = [ + "serde_core", +] + [[package]] name = "strsim" version = "0.11.1" @@ -326,20 +533,76 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termtree" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "toml" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + [[package]] name = "trait-winnower" version = "0.1.0" dependencies = [ "anyhow", "assert_cmd", + "assert_fs", "clap", + "ignore", "predicates", + "serde", + "toml", ] [[package]] @@ -363,6 +626,43 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-link" version = "0.1.3" @@ -442,3 +742,15 @@ name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/Cargo.toml b/Cargo.toml index c19b0bc..556d987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,15 @@ edition = "2024" [dependencies] clap = { version = "4.5.46", features = ["derive"] } anyhow = "1.0.99" +ignore = "0.4.23" +serde = { version = "1.0.225", features = ["derive"] } +toml = "0.9.6" [dev-dependencies] assert_cmd = "2.0.17" -predicates = "3.1.3" \ No newline at end of file +predicates = "3.1.3" +assert_fs = "1.1.3" + +[[bin]] +name = "trait-winnower" +path = "src/bin/trait-winnower.rs" \ No newline at end of file diff --git a/src/bin/trait-winnower.rs b/src/bin/trait-winnower.rs new file mode 100644 index 0000000..8c7e3e4 --- /dev/null +++ b/src/bin/trait-winnower.rs @@ -0,0 +1,51 @@ +// src/main.rs +//! Trait Winnower CLI binary. + +#![deny(missing_docs)] + +use clap::Parser; +use std::path::PathBuf; + +use trait_winnower::cli; +use trait_winnower::config::Config; +use trait_winnower::error::TraitError; +use trait_winnower::target::TargetKind; + +fn main() -> TraitError<()> { + let args = cli::Cli::parse(); + + match args.command { + // init: initializes project config (e.g., default path); + cli::Commands::Init { path, force } => { + // Default to current directory if not provided. + let mut root: PathBuf = path.unwrap_or_else(|| PathBuf::from(".")); + + if root.is_file() { + if let Some(parent) = root.parent() { + root = parent.to_path_buf(); + } + } + + let path_written = Config::write_default_config_at(root.as_path(), force)?; + println!( + "{} .trait-winnower.toml at {}", + if force { "Overwrote" } else { "Initialized" }, + path_written.display() + ); + } + + // prune: prunes undue/overly-strong trait bounds while preserving correctness. + cli::Commands::Prune { target } => { + let _kind = TargetKind::get_target(target)?; + // todo!(); + } + + // check: scans and warns about likely unnecessary trait bounds (no edits). + cli::Commands::Check { target } => { + let _kind = TargetKind::get_target(target)?; + // todo!(); + } + } + + Ok(()) +} diff --git a/src/cli.rs b/src/cli.rs index 8e96a6e..b651b19 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,8 @@ // src/cli.rs //! CLI argument parser for trait-winnower. +#![deny(missing_docs)] + use clap::{Parser, Subcommand}; use std::path::PathBuf; @@ -43,12 +45,12 @@ pub enum Commands { /// Prune undue/overly-strong trait bounds. Prune { /// Target to operate on. Defaults to ".". - target: Option, + target: Option, }, /// Check target and report likely unnecessary trait bounds. Check { /// Target to check. Defaults to ".". - target: Option, + target: Option, }, } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4954f2c --- /dev/null +++ b/src/config.rs @@ -0,0 +1,47 @@ +// src/config.rs +//! Configuration file for trait-winnower + +#![deny(missing_docs)] + +use crate::error::TraitError; +use serde::{Deserialize, Serialize}; +use std::{fs, path::Path, path::PathBuf}; + +/// Config struct for trait-winnower. +#[derive(Debug, Serialize, Deserialize)] +pub struct Config { + /// Include files. + pub include: Vec, + /// Exclude files. + pub exclude: Vec, +} + +impl Default for Config { + fn default() -> Self { + Self { + include: vec!["**/*.rs".into()], + exclude: vec![ + "target/**".into(), + "**/.git/**".into(), + "**/tests/**".into(), + ], + } + } +} + +impl Config { + /// Write default configs to .trait-winnower.toml + pub fn write_default_config_at(dir: &Path, force: bool) -> TraitError { + let base = if dir.is_file() { + dir.parent().unwrap_or(dir) + } else { + dir + }; + let file = base.join(".trait-winnower.toml"); + if !file.exists() || force { + let s = toml::to_string_pretty(&Self::default())?; + fs::write(&file, s)?; + } + Ok(file) + } +} diff --git a/src/discover.rs b/src/discover.rs new file mode 100644 index 0000000..fbcd1e2 --- /dev/null +++ b/src/discover.rs @@ -0,0 +1,50 @@ +// src/error.rs +//! Source targets for trait-winnower. + +#![deny(missing_docs)] + +use crate::error::TraitError; +use ignore::WalkBuilder; +use std::path::{Path, PathBuf}; + +/// Discover struct to keep +pub struct Discover(); + +impl Discover { + /// Find the files to operate on. + pub fn discover_rs_files(root: &Path) -> TraitError> { + let mut paths = Vec::new(); + let mut builder = WalkBuilder::new(root); + + builder + .hidden(false) + .ignore(true) + .git_ignore(true) + .git_exclude(true) + .git_global(true) + .follow_links(false) + .max_depth(None); + + builder.add_ignore(".git"); + builder.add_ignore("target"); + builder.add_ignore("node_modules"); + builder.add_ignore("tests"); + + for res in builder.build() { + let dent = match res { + Ok(d) => d, + Err(_) => continue, + }; + + if !dent.file_type().map(|t| t.is_file()).unwrap_or(false) { + continue; + } + + if dent.path().extension().and_then(|s| s.to_str()) == Some("rs") { + paths.push(dent.into_path()); + } + } + + Ok(paths) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..9c67f31 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,7 @@ +// src/error.rs +//! Error handling for trait-winnower. + +#![deny(missing_docs)] + +/// TraitError is alias for anyhow +pub type TraitError = anyhow::Result; diff --git a/src/lib.rs b/src/lib.rs index 8ce6819..b8d9d1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,10 @@ // src/lib.rs //! Trait Winnower library. +#![deny(missing_docs)] + pub mod cli; +pub mod config; +pub mod discover; +pub mod error; +pub mod target; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e040a28..0000000 --- a/src/main.rs +++ /dev/null @@ -1,31 +0,0 @@ -// src/main.rs -//! Trait Winnower CLI binary. - -use anyhow::Result; -use clap::Parser; -use trait_winnower::cli; - -fn main() -> Result<()> { - let args = cli::Cli::parse(); - - match args.command { - // init: initializes project config (e.g., default path); - cli::Commands::Init { .. } => { - // todo!(); - } - - // prune: prunes undue/overly-strong trait bounds while preserving correctness. - cli::Commands::Prune { target } => { - let _target = target.unwrap_or_else(|| ".".to_string()); - // todo!(); - } - - // check: scans and warns about likely unnecessary trait bounds (no edits). - cli::Commands::Check { target } => { - let _target = target.unwrap_or_else(|| ".".to_string()); - // todo!(); - } - } - - Ok(()) -} diff --git a/src/target.rs b/src/target.rs new file mode 100644 index 0000000..77f488a --- /dev/null +++ b/src/target.rs @@ -0,0 +1,50 @@ +// src/error.rs +//! Source targets for trait-winnower. + +#![deny(missing_docs)] + +use crate::error::TraitError; +use anyhow::{Context, bail}; +use std::fs; +use std::path::PathBuf; + +/// The classification of a target path. +#[derive(Debug)] +pub enum TargetKind { + /// A single `.rs` file. + SingleFile(PathBuf), + /// A crate with a `Cargo.toml`. + Crate(PathBuf), + /// A workspace root with a `Cargo.toml` that contains `[workspace]`. + Workspace(PathBuf), +} + +impl TargetKind { + /// Resolve the user-provided target (file or directory). + pub fn get_target(raw: Option) -> TraitError { + let path = raw.unwrap_or_else(|| PathBuf::from(".")); + let meta = + fs::metadata(&path).with_context(|| format!("target not found: {}", path.display()))?; + + if meta.is_file() { + if path.extension().and_then(|s| s.to_str()) != Some("rs") { + bail!("single-file mode requires a .rs file: {}", path.display()); + } + return Ok(TargetKind::SingleFile(path)); + } + + let cargo = path.join("Cargo.toml"); + if !cargo.exists() { + bail!( + "no Cargo.toml in {}. Provide a crate root or a single Rust file", + path.display() + ); + } + let toml = fs::read_to_string(&cargo).unwrap_or_default(); + if toml.contains("[workspace]") { + Ok(TargetKind::Workspace(path)) + } else { + Ok(TargetKind::Crate(path)) + } + } +} diff --git a/tests/cli.rs b/tests/cli.rs index 6cb2e47..b4f369c 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -2,7 +2,12 @@ //! Trait Winnower CLI tests. use assert_cmd::Command; +use assert_fs::assert::PathAssert; +use assert_fs::fixture::FileWriteStr; +use assert_fs::fixture::PathChild; +use assert_fs::fixture::PathCreateDir; use predicates::str::contains; +use trait_winnower::config::Config; type TestResult = Result<(), Box>; @@ -19,3 +24,62 @@ fn dies_no_args() -> TestResult { Ok(()) } + +#[test] +fn init_writes_default_config_in_cwd() -> Result<(), Box> { + let tmp = assert_fs::TempDir::new()?; + + Command::cargo_bin("trait-winnower")? + .current_dir(&tmp) + .arg("init") + .assert() + .success(); + + let cfg_path = tmp.child(".trait-winnower.toml"); + cfg_path.assert(predicates::path::exists()); + + let s = std::fs::read_to_string(cfg_path.path())?; + let cfg: Config = toml::from_str(&s)?; + let def = Config::default(); + assert_eq!(cfg.include, def.include); + assert_eq!(cfg.exclude, def.exclude); + + tmp.close()?; + Ok(()) +} + +#[test] +fn check_dry_run_on_crate_root_succeeds() -> Result<(), Box> { + let tmp = assert_fs::TempDir::new()?; + tmp.child("Cargo.toml") + .write_str("[package]\nname=\"x\"\nversion=\"0.1.0\"\n")?; + tmp.child("src").create_dir_all()?; + tmp.child("src/lib.rs").write_str("// lib\n")?; + + Command::cargo_bin("trait-winnower")? + .current_dir(&tmp) + .args(["check", "."]) + .assert() + .success(); + + tmp.close()?; + Ok(()) +} + +#[test] +fn prune_dry_run_on_crate_root_succeeds() -> Result<(), Box> { + let tmp = assert_fs::TempDir::new()?; + tmp.child("Cargo.toml") + .write_str("[package]\nname=\"x\"\nversion=\"0.1.0\"\n")?; + tmp.child("src").create_dir_all()?; + tmp.child("src/lib.rs").write_str("// lib\n")?; + + Command::cargo_bin("trait-winnower")? + .current_dir(&tmp) + .args(["prune", "."]) + .assert() + .success(); + + tmp.close()?; + Ok(()) +} diff --git a/tests/resolve_tests.rs b/tests/resolve_tests.rs new file mode 100644 index 0000000..bf6e81b --- /dev/null +++ b/tests/resolve_tests.rs @@ -0,0 +1,49 @@ +use assert_fs::prelude::*; +use std::path::PathBuf; +use trait_winnower::target::TargetKind; + +#[test] +fn resolves_single_rs_file() -> Result<(), Box> { + let tmp = assert_fs::TempDir::new()?; + let f = tmp.child("main.rs"); + f.write_str("// hi")?; + let kind = TargetKind::get_target(Some(PathBuf::from(f.path())))?; + match kind { + TargetKind::SingleFile(p) => assert_eq!(p, PathBuf::from(f.path())), + _ => panic!("expected SingleFile"), + } + tmp.close()?; + Ok(()) +} + +#[test] +fn rejects_non_rs_file() { + let tmp = assert_fs::TempDir::new().unwrap(); + let f = tmp.child("README.txt"); + f.write_str("x").unwrap(); + let err = TargetKind::get_target(Some(PathBuf::from(f.path()))).unwrap_err(); + let msg = format!("{err}"); + assert!(msg.contains(".rs file"), "got: {msg}"); +} + +#[test] +fn resolves_crate_root() -> Result<(), Box> { + let tmp = assert_fs::TempDir::new()?; + tmp.child("Cargo.toml") + .write_str("[package]\nname=\"x\"\nversion=\"0.1.0\"\n")?; + let kind = TargetKind::get_target(Some(PathBuf::from(tmp.path())))?; + matches!(kind, TargetKind::Crate(_)) + .then_some(()) + .ok_or("not Crate".into()) +} + +#[test] +fn resolves_workspace_root() -> Result<(), Box> { + let tmp = assert_fs::TempDir::new()?; + tmp.child("Cargo.toml") + .write_str("[workspace]\nmembers=[]\n")?; + let kind = TargetKind::get_target(Some(PathBuf::from(tmp.path())))?; + matches!(kind, TargetKind::Workspace(_)) + .then_some(()) + .ok_or("not Workspace".into()) +}