From ed7163a63252bff56b629d52a3f7f5e77d644088 Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Fri, 10 Mar 2023 00:30:45 -0500 Subject: [PATCH 01/14] Support binary crates (Linux/Mac) --- examples/hello-world-axum/Cargo.toml | 11 ++++ examples/hello-world-axum/src/main.rs | 17 +++++ src/constants.rs | 2 + src/lib.rs | 4 +- src/models/path.rs | 91 +++++++++++++++++++-------- src/pathbufext.rs | 21 +++++++ src/run_ext.rs | 17 +++-- src/run_impl.rs | 28 +++++---- 8 files changed, 148 insertions(+), 43 deletions(-) create mode 100644 examples/hello-world-axum/Cargo.toml create mode 100644 examples/hello-world-axum/src/main.rs create mode 100644 src/pathbufext.rs diff --git a/examples/hello-world-axum/Cargo.toml b/examples/hello-world-axum/Cargo.toml new file mode 100644 index 0000000..91fc1ab --- /dev/null +++ b/examples/hello-world-axum/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hello-world-axum" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +axum = { version = "0.6.10" } +tokio = { version = "1.0", features = ["full"] } diff --git a/examples/hello-world-axum/src/main.rs b/examples/hello-world-axum/src/main.rs new file mode 100644 index 0000000..24df4ac --- /dev/null +++ b/examples/hello-world-axum/src/main.rs @@ -0,0 +1,17 @@ +//! This example is a custom use-case where we have Cargo +//! (binary) crates within the `examples/` folder. +//! +//! # Note +//! +//! This is a use case that not even `cargo run --example` +//! currently supports -- in fact, this is not even +//! detected as an example otherwise! +//! +//! # Sample Projects With This Setup +//! +//! * [axum](https://github.com/tokio-rs/axum) +//! * [tract](https://github.com/sonos/tract) +//! +fn main() { + println!("Hello, world!"); +} diff --git a/src/constants.rs b/src/constants.rs index 6d9508d..3871efe 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -14,6 +14,8 @@ pub const RUST_FILE_EXT: &str = "rs"; /// Name of the `Cargo.toml` file in Cargo projects pub const CARGO_TOML: &str = "Cargo.toml"; +/// Name of the `main.rs` file in Cargo projects +pub const MAIN_RS: &str = "main.rs"; /// Name of the `settings.toml` file for local project cache pub const SETTINGS_TOML: &str = "settings.toml"; diff --git a/src/lib.rs b/src/lib.rs index 00f2ef8..050e6a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ mod constants; mod models; // noinspection SpellCheckingInspection mod osstringext; +mod pathbufext; mod run_ext; mod run_impl; mod types; @@ -96,6 +97,7 @@ use cache::*; pub use constants::*; pub use models::*; pub use osstringext::*; +pub use pathbufext::*; pub use run_ext::*; pub(crate) use run_impl::*; pub use types::*; @@ -112,7 +114,7 @@ pub fn process_input(args: Args) -> Result<()> { let name_to_required_features = p.example_to_required_features()?; let files = p.example_file_paths()?; - process_input_inner(files, p, args, name_to_required_features) + process_input_inner(files, &p, args, name_to_required_features) } /// This is a **patch** so that the `colored` output works as expected diff --git a/src/models/path.rs b/src/models/path.rs index 6813986..ba52e5b 100644 --- a/src/models/path.rs +++ b/src/models/path.rs @@ -1,7 +1,8 @@ use crate::*; +use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap}; use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::io::{Error, ErrorKind}; @@ -12,6 +13,22 @@ use std::{env, fs}; use cargo_toml::Manifest; use path_absolutize::*; +/// Allows creating a Path with a Builder Pattern +/// +/// Credits: https://internals.rust-lang.org/t/add-zero-cost-builder-methods-to-pathbuf/15318/16?u=rnag +macro_rules! path { + ( $($segment:expr),+ ) => {{ + let mut path = ::std::path::PathBuf::new(); + $(path.push($segment);)* + path + }}; + ( $($segment:expr),+; capacity = $n:expr ) => {{ + let mut path = ::std::path::PathBuf::with_capacity($n); + $(path.push($segment);)* + path + }}; +} + /// Represents *path info* on a Cargo project. #[derive(Debug)] pub struct Paths { @@ -40,6 +57,12 @@ pub enum ExampleType { /// example. MultiFile, + /// This represents a binary crate, i.e. a sub-folder containing a + /// `Cargo.toml` file in an `examples/` folder; Note that Cargo (and + /// notably `cargo run --example`) does not support this particular + /// use-case, as of yet. + Crate(PathBuf), + /// This represents an example file with a *custom path* defined in the /// `Cargo.toml` file of a Cargo project. Custom, @@ -98,28 +121,46 @@ impl TryFrom for ExampleFile { type Error = Error; /// Try to create an `ExampleFile` from a `PathBuf` object. - fn try_from(mut path: PathBuf) -> StdResult { + fn try_from(path: PathBuf) -> StdResult { let file_type = path.metadata()?.file_type(); if file_type.is_dir() { - path.push("main.rs"); - if path.is_file() { - let mut comps = path.components(); - // discard the filename (main.rs) - let _ = comps.next_back(); - // return the parent folder name - let name = comps - .next_back() - .unwrap() - .as_os_str() - .to_str() - .unwrap() - .to_owned(); + let main_rs = path.join(MAIN_RS); + if main_rs.is_file() { return Ok(Self { + // return the parent folder name + name: path.last(), + path: main_rs, + path_type: ExampleType::MultiFile, + }); + } + + let cargo_toml = path.join(CARGO_TOML); + + if cargo_toml.is_file() { + // the parent folder name + let name = path.last(); + + // TODO is length ok? + let main_rs = path!( + path, + "src", + MAIN_RS; + capacity = path.as_os_str().len() + 10 + ); + + let path = if main_rs.is_file() { + main_rs + } else { + cargo_toml.clone() + }; + + return Ok(Self { + // return the parent folder name name, path, - path_type: ExampleType::MultiFile, + path_type: ExampleType::Crate(cargo_toml), }); } } else if file_type.is_file() && matches!(path.extension(), Some(e) if e == RUST_FILE_EXT) { @@ -177,8 +218,8 @@ impl Paths { let examples_folder = Path::new(EXAMPLES_FOLDER); let cargo_toml_file = Path::new(CARGO_TOML); - let examples_path = current_dir.join(&examples_folder); - let cargo_toml_path = current_dir.join(&cargo_toml_file); + let examples_path = current_dir.join(examples_folder); + let cargo_toml_path = current_dir.join(cargo_toml_file); if examples_path.is_dir() && cargo_toml_path.is_file() { let manifest_contents = fs::read(&cargo_toml_path)?; @@ -199,8 +240,8 @@ impl Paths { Component::Normal(_) | Component::CurDir | Component::ParentDir => { let root_path = comps.as_path().to_path_buf(); - let examples_path = root_path.join(&examples_folder); - let cargo_toml_path = root_path.join(&cargo_toml_file); + let examples_path = root_path.join(examples_folder); + let cargo_toml_path = root_path.join(cargo_toml_file); if examples_path.is_dir() && cargo_toml_path.is_file() { let manifest_contents = fs::read(&cargo_toml_path)?; @@ -229,6 +270,7 @@ impl Paths { } /// Return a mapping of *example name* to a list of *required features* for an example. + // TODO merge with below! pub fn example_to_required_features(&self) -> Result> { let mut name_to_required_features: HashMap = HashMap::with_capacity(self.manifest.example.len()); @@ -248,8 +290,8 @@ impl Paths { } /// Returns file paths (`PathBuf` objects) of each *example* file in the Cargo project. - pub fn example_file_paths(&self) -> Result> { - let mut files = HashSet::new(); + pub fn example_file_paths(&self) -> Result, ExampleFile>> { + let mut files: BTreeMap, ExampleFile> = BTreeMap::new(); let manifest = &self.manifest; let root = &self.root_path; @@ -264,9 +306,8 @@ impl Paths { // #[cfg(not(target_family = "windows"))] // let path = path.replace('\\', "/"); - let f = ExampleFile::from_name_and_path(root, name.to_owned(), path); - files.insert(f); + files.insert(Cow::Borrowed(name), f); } } } @@ -275,7 +316,7 @@ impl Paths { let path: PathBuf = entry.path(); if let Ok(f) = ExampleFile::try_from(path) { - files.insert(f); + files.insert(Cow::Owned(f.name.to_owned()), f); } } diff --git a/src/pathbufext.rs b/src/pathbufext.rs new file mode 100644 index 0000000..5fed268 --- /dev/null +++ b/src/pathbufext.rs @@ -0,0 +1,21 @@ +//! Extensions for `PathBuf` +//! +use std::path::PathBuf; + +#[doc(hidden)] +pub trait PathBufExt { + fn last(&self) -> String; +} + +impl PathBufExt for PathBuf { + /// returns the last component of a `PathBuf`, as a string. + fn last(&self) -> String { + self.components() + .next_back() + .unwrap() + .as_os_str() + .to_str() + .unwrap() + .to_owned() + } +} diff --git a/src/run_ext.rs b/src/run_ext.rs index 39f3989..2278018 100644 --- a/src/run_ext.rs +++ b/src/run_ext.rs @@ -13,6 +13,7 @@ pub trait RunExampleExt { /// /// # Arguments /// + /// * `ex_type` - Type of example. /// * `root_path` - the base path to the Cargo directory with a /// `Cargo.toml` file. /// * `name` - the name of the Cargo example to run. @@ -21,6 +22,7 @@ pub trait RunExampleExt { /// the example. fn run_example<'a, T: IntoIterator>( &self, + ex_type: &'a ExampleType, root_path: &'a Path, name: &'a str, args: T, @@ -32,13 +34,20 @@ pub trait RunExampleExt { /// Add `run --example ` as arguments to Command `cmd` #[inline] -fn add_run_example(cmd: &mut Command, name: &str) { - cmd.arg("run").arg("--example").arg(name); +fn add_run_arg(cmd: &mut Command, name: &str, ex_type: &ExampleType, root_path: &Path) { + if let ExampleType::Crate(manifest_path) = ex_type { + cmd.arg("run") + .arg("--manifest-path") + .arg(manifest_path.strip_prefix(root_path).unwrap()); + } else { + cmd.arg("run").arg("--example").arg(name); + } } impl RunExampleExt for CommonOptions { fn run_example<'a, T: IntoIterator>( &self, + ex_type: &'a ExampleType, root_path: &'a Path, name: &'a str, args: T, @@ -58,11 +67,11 @@ impl RunExampleExt for CommonOptions { let has_unstable_opts = has_config || has_unstable_flags || self.unit_graph; if !has_unstable_opts { - add_run_example(&mut run, name); + add_run_arg(&mut run, name, ex_type, root_path); } else { // enable the `+nightly` toolchain run.arg("+nightly"); - add_run_example(&mut run, name); + add_run_arg(&mut run, name, ex_type, root_path); // enable the `unstable-options` run.arg("-Z").arg("unstable-options"); } diff --git a/src/run_impl.rs b/src/run_impl.rs index b273106..a86ac14 100644 --- a/src/run_impl.rs +++ b/src/run_impl.rs @@ -127,7 +127,7 @@ mod inner_impl { use super::*; use std::borrow::Cow; - use std::collections::{HashMap, HashSet}; + use std::collections::{BTreeMap, HashMap}; use std::io::Write; use std::sync::Arc; @@ -135,8 +135,8 @@ mod inner_impl { //noinspection DuplicatedCode pub(crate) fn process_input_inner( - example_files: HashSet, - dir: Paths, + example_files: BTreeMap, ExampleFile>, + dir: &Paths, args: Args, name_to_required_features: HashMap, ) -> Result<()> { @@ -150,11 +150,6 @@ mod inner_impl { } else if let Some(example) = args.name { vec![Cow::Owned(example)] } else { - let mut example_files: Vec<_> = Vec::from_iter(example_files); - - // Sort A -> Z, using the names of example files - example_files.sort_unstable(); - let options = SkimOptionsBuilder::default() // .height(Some("50%")) .preview_window(Some("right:70%")) @@ -165,10 +160,10 @@ mod inner_impl { let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = unbounded(); - for ex_file in example_files.into_iter() { + for ex_file in example_files.values() { let _ = tx_item.send(Arc::new(ExampleFileItem { - file_stem: ex_file.name, - file_path: ex_file.path, + file_stem: ex_file.name.clone(), + file_path: ex_file.path.clone(), })); } drop(tx_item); // so that skim could know when to stop waiting for more items. @@ -233,12 +228,19 @@ mod inner_impl { }; for example in examples { + let ex_file = example_files.get(&example).unwrap(); + let name = example.as_ref(); let req_features: Option<&String> = name_to_required_features.get(name); // Run the Cargo example script - args.cargo - .run_example(root_ref, name, example_args_ref, req_features)?; + args.cargo.run_example( + &ex_file.path_type, + root_ref, + name, + example_args_ref, + req_features, + )?; } Ok(()) From 051d689ec7da0255e6bfd150436d64adae959f33 Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Fri, 10 Mar 2023 09:33:50 -0500 Subject: [PATCH 02/14] minor changes --- src/lib.rs | 2 +- src/models/path.rs | 11 ++++++----- src/run_impl.rs | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 050e6a1..b0e1de3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ pub fn process_input(args: Args) -> Result<()> { let p = Paths::resolve()?; let name_to_required_features = p.example_to_required_features()?; - let files = p.example_file_paths()?; + let files = p.example_file_name_to_path()?; process_input_inner(files, &p, args, name_to_required_features) } diff --git a/src/models/path.rs b/src/models/path.rs index ba52e5b..95bdde6 100644 --- a/src/models/path.rs +++ b/src/models/path.rs @@ -142,12 +142,13 @@ impl TryFrom for ExampleFile { // the parent folder name let name = path.last(); - // TODO is length ok? let main_rs = path!( path, "src", MAIN_RS; - capacity = path.as_os_str().len() + 10 + // "srcmain.rs".len() == 10 + // adding +2 because of slashes (/) between components + capacity = path.as_os_str().len() + 12 ); let path = if main_rs.is_file() { @@ -157,7 +158,6 @@ impl TryFrom for ExampleFile { }; return Ok(Self { - // return the parent folder name name, path, path_type: ExampleType::Crate(cargo_toml), @@ -289,8 +289,9 @@ impl Paths { Ok(name_to_required_features) } - /// Returns file paths (`PathBuf` objects) of each *example* file in the Cargo project. - pub fn example_file_paths(&self) -> Result, ExampleFile>> { + /// Returns an ordered (A -> Z) mapping of file name to paths (`PathBuf` + /// objects) of each *example* file in the Cargo project. + pub fn example_file_name_to_path(&self) -> Result, ExampleFile>> { let mut files: BTreeMap, ExampleFile> = BTreeMap::new(); let manifest = &self.manifest; diff --git a/src/run_impl.rs b/src/run_impl.rs index a86ac14..c38f9f2 100644 --- a/src/run_impl.rs +++ b/src/run_impl.rs @@ -135,7 +135,7 @@ mod inner_impl { //noinspection DuplicatedCode pub(crate) fn process_input_inner( - example_files: BTreeMap, ExampleFile>, + example_file_name_to_path: BTreeMap, ExampleFile>, dir: &Paths, args: Args, name_to_required_features: HashMap, @@ -144,7 +144,7 @@ mod inner_impl { let selected_items: Vec>; let mut cfg: ReplayConfig = Default::default(); - let examples = if args.replay { + let examples_to_run = if args.replay { cfg = get_last_replay()?; vec![Cow::Owned(cfg.last_run.name)] } else if let Some(example) = args.name { @@ -160,10 +160,10 @@ mod inner_impl { let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = unbounded(); - for ex_file in example_files.values() { + for example in example_file_name_to_path.values() { let _ = tx_item.send(Arc::new(ExampleFileItem { - file_stem: ex_file.name.clone(), - file_path: ex_file.path.clone(), + file_stem: example.name.clone(), + file_path: example.path.clone(), })); } drop(tx_item); // so that skim could know when to stop waiting for more items. @@ -220,22 +220,22 @@ mod inner_impl { let root_ref = &dir.root_path; // Save info on the example we're running, so we can `--replay` it if needed - match examples.first() { + match examples_to_run.first() { Some(name) if !args.replay => { save_last_replay(name, example_args_ref)?; } _ => {} }; - for example in examples { - let ex_file = example_files.get(&example).unwrap(); + for example_name in examples_to_run { + let name = example_name.as_ref(); - let name = example.as_ref(); + let example = example_file_name_to_path.get(name).unwrap(); let req_features: Option<&String> = name_to_required_features.get(name); // Run the Cargo example script args.cargo.run_example( - &ex_file.path_type, + &example.path_type, root_ref, name, example_args_ref, From 028b0693f9b99903ffe678c61e58aff447b9e378 Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Fri, 10 Mar 2023 13:49:08 -0500 Subject: [PATCH 03/14] merge `Paths::example_to_required_features` with `Paths::example_file_name_to_path` --- src/lib.rs | 3 +- src/models/path.rs | 92 ++++++++++++++++++++++++++++++++-------------- src/run_ext.rs | 4 +- src/run_impl.rs | 7 +--- 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b0e1de3..471cf75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,10 +111,9 @@ pub fn process_input(args: Args) -> Result<()> { let p = Paths::resolve()?; - let name_to_required_features = p.example_to_required_features()?; let files = p.example_file_name_to_path()?; - process_input_inner(files, &p, args, name_to_required_features) + process_input_inner(files, &p, args) } /// This is a **patch** so that the `colored` output works as expected diff --git a/src/models/path.rs b/src/models/path.rs index 95bdde6..a6e1780 100644 --- a/src/models/path.rs +++ b/src/models/path.rs @@ -2,7 +2,7 @@ use crate::*; use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::io::{Error, ErrorKind}; @@ -10,7 +10,7 @@ use std::path::{Component, Path, PathBuf}; use std::result::Result as StdResult; use std::{env, fs}; -use cargo_toml::Manifest; +use cargo_toml::{Manifest, Product}; use path_absolutize::*; /// Allows creating a Path with a Builder Pattern @@ -81,6 +81,15 @@ pub struct ExampleFile { /// Type of example file pub path_type: ExampleType, + + /// A space-separated list of required features for the example (or binary) to run. + /// + /// The required-features field specifies which features the product needs in order to be built. + /// + /// If any of the required features are not selected, the product will be skipped. + /// This is only relevant for the `[[bin]]`, `[[bench]]`, `[[test]]`, and `[[example]]` sections, + /// it has no effect on `[lib]`. + pub required_features: Option, } /// *order* a sequence of `ExampleFile`s by the `name` field. @@ -133,6 +142,7 @@ impl TryFrom for ExampleFile { name: path.last(), path: main_rs, path_type: ExampleType::MultiFile, + required_features: None, }); } @@ -161,6 +171,7 @@ impl TryFrom for ExampleFile { name, path, path_type: ExampleType::Crate(cargo_toml), + required_features: None, }); } } else if file_type.is_file() && matches!(path.extension(), Some(e) if e == RUST_FILE_EXT) { @@ -170,6 +181,7 @@ impl TryFrom for ExampleFile { name, path, path_type: ExampleType::Simple, + required_features: None, }); } @@ -179,8 +191,8 @@ impl TryFrom for ExampleFile { } impl ExampleFile { - /// Create an `ExampleFile` from a example `name` and a (possibly relative) - /// `path`. + /// Create an `ExampleFile` from a example `name`, a (possibly relative) + /// `path`, and a list of *required features* for the example. /// /// # Arguments /// * `root` - The current working directory, used in case the path is @@ -188,7 +200,12 @@ impl ExampleFile { /// * `name` - The name of the example file, without the extension /// * `path` - The path to the example file. This can be a relative path /// and contain characters such as `.` and `..` for instance. - pub fn from_name_and_path>(root: &Path, name: String, path: P) -> Self { + pub fn new>( + root: &Path, + name: String, + path: P, + required_features: Option, + ) -> Self { let abs_path = path.as_ref().absolutize_from(root).unwrap(); let path = PathBuf::from(abs_path); @@ -196,6 +213,7 @@ impl ExampleFile { name, path, path_type: ExampleType::Custom, + required_features, } } } @@ -269,35 +287,26 @@ impl Paths { ))) } - /// Return a mapping of *example name* to a list of *required features* for an example. - // TODO merge with below! - pub fn example_to_required_features(&self) -> Result> { - let mut name_to_required_features: HashMap = - HashMap::with_capacity(self.manifest.example.len()); - let manifest = &self.manifest; - - for example in manifest.example.iter() { - match example.name { - Some(ref name) if !example.required_features.is_empty() => { - name_to_required_features - .insert(name.clone(), example.required_features.join(" ")); - } - _ => (), - }; - } - - Ok(name_to_required_features) - } - /// Returns an ordered (A -> Z) mapping of file name to paths (`PathBuf` /// objects) of each *example* file in the Cargo project. pub fn example_file_name_to_path(&self) -> Result, ExampleFile>> { let mut files: BTreeMap, ExampleFile> = BTreeMap::new(); + let mut file_paths: HashSet = HashSet::new(); + + #[inline] + fn required_features(example: &Product) -> Option { + if !example.required_features.is_empty() { + Some(example.required_features.join(" ")) + } else { + None + } + } let manifest = &self.manifest; let root = &self.root_path; for example in manifest.example.iter() { + // only if `name` and `path` are both provided if let Some(ref path) = example.path { if let Some(ref name) = example.name { // I debated whether to add this for Mac/Linux, however it @@ -307,7 +316,19 @@ impl Paths { // #[cfg(not(target_family = "windows"))] // let path = path.replace('\\', "/"); - let f = ExampleFile::from_name_and_path(root, name.to_owned(), path); + let f = + ExampleFile::new(root, name.to_owned(), path, required_features(example)); + + file_paths.insert(f.path.clone()); + files.insert(Cow::Borrowed(name), f); + } + } + // only if `name` and `required-features` are both provided + else if let Some(ref name) = example.name { + let required_features = required_features(example); + + if required_features.is_some() { + let f = ExampleFile::new(root, name.to_owned(), "N/A", required_features); files.insert(Cow::Borrowed(name), f); } } @@ -316,8 +337,25 @@ impl Paths { for entry in fs::read_dir(&self.examples_path)?.filter_map(StdResult::ok) { let path: PathBuf = entry.path(); + // if a file path is already specified in the `Cargo.toml`, then this + // is an `ExampleType::Custom`, so we don't need to check the file. + if file_paths.contains(&path) { + continue; + } + if let Ok(f) = ExampleFile::try_from(path) { - files.insert(Cow::Owned(f.name.to_owned()), f); + let key = Cow::Owned(f.name.to_owned()); + + // if the example name already exists in `Cargo.toml`, then + // just update values as needed. + if let Some(example) = files.get_mut(&key) { + example.path = f.path; + example.path_type = f.path_type; + } + // else, we record and add a new example file that can be run. + else { + files.insert(key, f); + } } } diff --git a/src/run_ext.rs b/src/run_ext.rs index 2278018..6122aae 100644 --- a/src/run_ext.rs +++ b/src/run_ext.rs @@ -26,7 +26,7 @@ pub trait RunExampleExt { root_path: &'a Path, name: &'a str, args: T, - required_features: Option<&'a String>, + required_features: &'a Option, ) -> Result<()> where ::Item: AsRef; @@ -51,7 +51,7 @@ impl RunExampleExt for CommonOptions { root_path: &'a Path, name: &'a str, args: T, - required_features: Option<&'a String>, + required_features: &'a Option, ) -> Result<()> where ::Item: AsRef, diff --git a/src/run_impl.rs b/src/run_impl.rs index c38f9f2..8d1686c 100644 --- a/src/run_impl.rs +++ b/src/run_impl.rs @@ -127,7 +127,7 @@ mod inner_impl { use super::*; use std::borrow::Cow; - use std::collections::{BTreeMap, HashMap}; + use std::collections::BTreeMap; use std::io::Write; use std::sync::Arc; @@ -138,7 +138,6 @@ mod inner_impl { example_file_name_to_path: BTreeMap, ExampleFile>, dir: &Paths, args: Args, - name_to_required_features: HashMap, ) -> Result<()> { let script_args = args.args; let selected_items: Vec>; @@ -229,9 +228,7 @@ mod inner_impl { for example_name in examples_to_run { let name = example_name.as_ref(); - let example = example_file_name_to_path.get(name).unwrap(); - let req_features: Option<&String> = name_to_required_features.get(name); // Run the Cargo example script args.cargo.run_example( @@ -239,7 +236,7 @@ mod inner_impl { root_ref, name, example_args_ref, - req_features, + &example.required_features, )?; } From 6fe1783006504ef7b497bfc487b987e2e5fd3f04 Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Fri, 10 Mar 2023 14:17:23 -0500 Subject: [PATCH 04/14] minor changes --- src/models/path.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/models/path.rs b/src/models/path.rs index a6e1780..72c41a4 100644 --- a/src/models/path.rs +++ b/src/models/path.rs @@ -290,15 +290,15 @@ impl Paths { /// Returns an ordered (A -> Z) mapping of file name to paths (`PathBuf` /// objects) of each *example* file in the Cargo project. pub fn example_file_name_to_path(&self) -> Result, ExampleFile>> { - let mut files: BTreeMap, ExampleFile> = BTreeMap::new(); + let mut files: BTreeMap, _> = BTreeMap::new(); let mut file_paths: HashSet = HashSet::new(); #[inline] fn required_features(example: &Product) -> Option { - if !example.required_features.is_empty() { - Some(example.required_features.join(" ")) - } else { + if example.required_features.is_empty() { None + } else { + Some(example.required_features.join(" ")) } } From ed29b3552fa3948fb7aa29fae3c562b786f9bfa5 Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Fri, 10 Mar 2023 16:28:44 -0500 Subject: [PATCH 05/14] add donation info to docs --- .github/FUNDING.yml | 2 ++ README.md | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..05daec8 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ + github: [rnag] + custom: ["https://www.buymeacoffee.com/ritviknag"] diff --git a/README.md b/README.md index 3df6c93..afaeb17 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,9 @@ **`cargo-rx` is a simple fuzzy finder and *R*unner for *Ex*amples in a [Cargo] project.** -[![rx demo](https://asciinema.org/a/483363.svg)](https://asciinema.org/a/483363) + + rx demo + [Cargo]: http://doc.crates.io/ @@ -15,6 +17,8 @@ This crate provides a single executable: `rx`. Basically anywhere you would use `cargo run --example` in a Rust project, try `rx` instead. +> **If this project has helped you, please consider making a [donation](https://www.buymeacoffee.com/ritviknag).** + @@ -91,6 +95,12 @@ Check out the [Contributing][] section in the docs for more info. [Contributing]: CONTRIBUTING.md [open an issue]: https://github.com/rnag/cargo-rx/issues +## Buy me a coffee + +Liked some of my work? Buy me a coffee (or more likely a beer) + +Buy Me A Coffee + ## License This project is proudly licensed under the MIT license ([LICENSE](LICENSE) From 1301661aa92d892f02f4e1ca873d5c916fdf8819 Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Fri, 10 Mar 2023 16:29:15 -0500 Subject: [PATCH 06/14] minor updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afaeb17..2bd0f3c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This crate provides a single executable: `rx`. Basically anywhere you would use `cargo run --example` in a Rust project, try `rx` instead. -> **If this project has helped you, please consider making a [donation](https://www.buymeacoffee.com/ritviknag).** +**If this project has helped you, please consider making a [donation](https://www.buymeacoffee.com/ritviknag).** +## v0.3.0 (2023-03-13) + +### Features +- Add support for [Cargo crates with binaries] within the `examples/` folder, + each containing their own `Cargo.toml` file. 🎉 + - This calls `cargo run --manifest-path ` internally, passing `--bin` in the case of multiple [binary targets]. + +[Cargo crates with binaries]: https://github.com/rnag/cargo-rx/issues/19 +[binary targets]: https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries + ## v0.2.0 (2022-04-25) ### Breaking Changes diff --git a/README.md b/README.md index fb4cc02..f76cd1e 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,13 @@ thus, the `fzf` tool serves as a stand-in alternative for now. * Fuzzy finder, which leverages [skim] to sort and search for *examples* in a Cargo project -- when called with just `rx`. * Pass arguments after `--` to the selected example. * Automatically [enables required-features] when running an example. +* Support for nested [crates with binary targets]. * Play back of most recently run example via the `--replay` option. * Automatically enables `+nightly` toolchain when passed in *unstable options* to `cargo run`, such as `--unit-graph`. [skim]: https://github.com/lotabout/skim [enables required-features]: https://github.com/rust-lang/cargo/issues/4663 +[crates with binary targets]: https://github.com/rnag/cargo-rx/issues/19 ## Contributing