add --infer that allows cargo-play to infer dependencies

This commit is contained in:
Zeyi Fan 2019-08-20 21:24:57 -07:00
parent 7e424d95b3
commit 8aff17b192
9 changed files with 150 additions and 1 deletions

38
Cargo.lock generated
View file

@ -80,10 +80,13 @@ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -192,6 +195,14 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.12"
@ -200,6 +211,14 @@ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.7.0"
@ -318,6 +337,16 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.10.1"
@ -371,6 +400,11 @@ name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
@ -419,7 +453,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3bf70094d203e07844da868b634207e71bfab254fe713171fae9a6e751ccf31"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5c2380ae88876faae57698be9e9775e3544decad214599c3a6266cca6ac802"
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca"
@ -435,6 +471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3d0760c312538987d363c36c42339b55f5ee176ea8808bbe4543d484a291c8d1"
"checksum structopt-derive 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "528aeb7351d042e6ffbc2a6fb76a86f9b622fdf7c25932798e7a82cb03bc94c6"
"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe"
"checksum syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "158521e6f544e7e3dcfc370ac180794aa38cb34a1b1e07609376d4adcf429b93"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
@ -442,6 +479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View file

@ -18,6 +18,9 @@ base64 = "0.10"
log = "0.4"
serde = { version = "1.0", features = ["derive"] }
pathdiff = "0.1.0"
proc-macro2 = "1.0.1"
syn = { version = "*", features = ["full"] }
quote = "1.0.2"
[dev-dependencies]
rand = "0.7.0"

19
fixtures/infer.rs Normal file
View file

@ -0,0 +1,19 @@
use bitflags::bitflags;
bitflags! {
struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
}
}
fn main() {
let e1 = Flags::A | Flags::C;
let e2 = Flags::B | Flags::C;
assert_eq!((e1 | e2), Flags::ABC); // union
assert_eq!((e1 & e2), Flags::C); // intersection
assert_eq!((e1 - e2), Flags::A); // set difference
assert_eq!(!e2, Flags::A); // set complement
}

View file

@ -18,6 +18,9 @@ pub enum CargoPlayError {
#[fail(display = "Path already exists at {:?}", _0)]
PathExistError(std::path::PathBuf),
#[fail(display = "Failed to parse source code: {:?}", _0)]
RustParseError(syn::Error),
/// Helper error kind only exists for development purpose.
#[fail(display = "{:?}", _0)]
_Message(String),
@ -29,6 +32,12 @@ impl From<std::io::Error> for CargoPlayError {
}
}
impl From<syn::Error> for CargoPlayError {
fn from(value: syn::Error) -> Self {
CargoPlayError::RustParseError(value)
}
}
impl CargoPlayError {
pub fn from_serde<T: Debug>(value: T) -> Self {
CargoPlayError::ParseError(format!("{:?}", value))

58
src/infer.rs Normal file
View file

@ -0,0 +1,58 @@
use std::collections::HashSet;
use std::fs;
use std::iter;
use std::path::PathBuf;
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::ToTokens;
use crate::errors::CargoPlayError;
const USE_KEYWORDS: &'static [&'static str] = &["std", "core", "crate", "self", "alloc", "super"];
fn extra_use<'a, T: 'a + IntoIterator<Item = TokenTree> + Clone>(
input: T,
) -> Box<dyn Iterator<Item = Ident> + 'a> {
use proc_macro2::Literal;
use TokenTree as tt;
Box::new(
input
.clone()
.into_iter()
.zip(
input
.into_iter()
.skip(1)
.chain(iter::once(Literal::u8_suffixed(1u8).into())),
)
.flat_map(|(prev, current)| match (prev, current) {
(tt::Ident(ref first), tt::Ident(ref second)) if first.to_string() == "use" => {
Box::new(iter::once(second.clone()))
}
(tt::Group(ref group), _) => extra_use(group.stream()),
_ => Box::new(iter::empty()),
}),
)
}
pub fn analyze_sources(sources: &Vec<PathBuf>) -> Result<HashSet<String>, CargoPlayError> {
let contents: Vec<_> = sources
.into_iter()
.map(fs::read_to_string)
.collect::<Result<_, _>>()?;
let streams: Vec<TokenStream> = contents
.into_iter()
.map(|file| -> Result<_, CargoPlayError> {
Ok(syn::parse_file(&file)?.into_token_stream())
})
.collect::<Result<_, CargoPlayError>>()?;
Ok(streams
.into_iter()
.flat_map(|token| extra_use(token.into_iter()))
.map(|ident| ident.to_string())
.filter(|ident| !USE_KEYWORDS.contains(&ident.as_ref()))
.collect())
}

View file

@ -1,4 +1,5 @@
mod cargo;
mod errors;
mod infer;
pub mod opt;
pub mod steps;

View file

@ -1,5 +1,6 @@
mod cargo;
mod errors;
mod infer;
mod opt;
mod steps;
@ -44,7 +45,15 @@ fn main() -> Result<(), CargoPlayError> {
}
let files = parse_inputs(&opt.src)?;
let dependencies = extract_headers(&files);
let mut dependencies = extract_headers(&files);
if opt.infer {
let mut infers = infer::analyze_sources(&opt.src)?
.into_iter()
.map(|crat| format!("{} = \"*\"", crat))
.collect();
dependencies.append(&mut infers);
}
if opt.clean {
rmtemp(&temp);

View file

@ -80,6 +80,9 @@ pub struct Opt {
#[structopt(long = "save")]
/// Generate a Cargo project based on inputs
pub save: Option<PathBuf>,
/// [experimental] Automatically infers dependency
#[structopt(long = "infer", short = "i")]
pub infer: bool,
#[structopt(multiple = true, last = true)]
/// Arguments passed to the underlying program
pub args: Vec<String>,

View file

@ -209,3 +209,12 @@ fn external_crate() -> Result<()> {
Ok(())
}
#[test]
fn simple_infer() -> Result<()> {
let rt = TestRuntime::new()?;
let output = rt.run(&["--infer", "fixtures/infer.rs"])?;
assert_eq!(output.status.code().unwrap(), 0);
Ok(())
}