diff --git a/src/consts.rs b/src/consts.rs index 3247281..8a377c0 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -14,8 +14,13 @@ pub const SCRIPT_BODY_SUB: &str = "script"; /// Substitution for the script prelude. pub const SCRIPT_PRELUDE_SUB: &str = "prelude"; -/// The template used for script file inputs. -pub const FILE_TEMPLATE: &str = r#"#{script}"#; +/// The template used for script file inputs that doesn't have main function. +pub const FILE_NO_MAIN_TEMPLATE: &str = r#" +fn main() -> Result<(), Box> { + {#{script}} + Ok(()) +} +"#; /// The template used for `--expr` input. pub const EXPR_TEMPLATE: &str = r#" diff --git a/src/main.rs b/src/main.rs index 7a4f7e8..19ba093 100644 --- a/src/main.rs +++ b/src/main.rs @@ -261,10 +261,11 @@ fn generate_package(action: &InputAction) -> MainResult<()> { info!("generating Cargo package..."); let mani_path = action.manifest_path(); - let script_path = action.script_path(); overwrite_file(&mani_path, &action.manifest)?; - overwrite_file(&script_path, &action.script)?; + if let Some(script) = &action.script { + overwrite_file(&action.script_path, script)?; + } info!("disarming pkg dir cleanup..."); cleanup_dir.disarm(); @@ -293,7 +294,8 @@ struct InputAction { /// Directory where the package should live. pkg_path: PathBuf, - script_name: String, + /// Path of the source code that Cargo.toml refers. + script_path: PathBuf, /** Is the package directory in the cache? @@ -315,8 +317,8 @@ struct InputAction { /// The package manifest contents. manifest: String, - /// The script source. - script: String, + /// The script source in case it has to be written. + script: Option, /// Did the user ask to run tests or benchmarks? build_kind: BuildKind, @@ -330,10 +332,6 @@ impl InputAction { self.pkg_path.join("Cargo.toml") } - fn script_path(&self) -> PathBuf { - self.pkg_path.join(&self.script_name) - } - fn cargo(&self, script_args: &[String]) -> MainResult { let release_mode = !self.debug && !matches!(self.build_kind, BuildKind::Bench); @@ -359,14 +357,15 @@ impl InputAction { }; if matches!(self.build_kind, BuildKind::Normal) && !self.force_compile { - let script_path = self.script_path(); - match fs::File::open(&built_binary_path) { Ok(built_binary_file) => { // Use ctime instead of mtime as cargo may copy an already // built binary (with old mtime) here: let built_binary_ctime = built_binary_file.metadata()?.created()?; - match (fs::File::open(script_path), fs::File::open(manifest_path)) { + match ( + fs::File::open(&self.script_path), + fs::File::open(manifest_path), + ) { (Ok(script_file), Ok(manifest_file)) => { let script_mtime = script_file.metadata()?.modified()?; let manifest_mtime = manifest_file.metadata()?.modified()?; @@ -470,10 +469,11 @@ fn decide_action_for( let script_name = format!("{}.rs", input.safe_name()); - let (mani_str, script_str) = manifest::split_input( + let (mani_str, script_path, script_str) = manifest::split_input( input, &deps, &prelude, + &pkg_path, &bin_name, &script_name, toolchain_version.clone(), @@ -491,7 +491,7 @@ fn decide_action_for( force_compile: args.force, execute: !args.gen_pkg_only, pkg_path, - script_name, + script_path, using_cache, toolchain_version, debug, diff --git a/src/manifest.rs b/src/manifest.rs index 224783b..f7464c4 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -6,6 +6,7 @@ use regex; use self::regex::Regex; use std::collections::HashMap; use std::path::Path; +use std::path::PathBuf; use crate::consts; use crate::error::{MainError, MainResult}; @@ -22,48 +23,53 @@ pub fn split_input( input: &Input, deps: &[(String, String)], prelude_items: &[String], + package_path: impl AsRef, bin_name: &str, script_name: &str, toolchain: Option, -) -> MainResult<(String, String)> { +) -> MainResult<(String, PathBuf, Option)> { fn contains_main_method(source: &str) -> bool { - let re_shebang: Regex = + let re_main: Regex = Regex::new(r#"(?m)^ *(pub )?(async )?(extern "C" )?fn main *\("#).unwrap(); - re_shebang.is_match(source) + re_main.is_match(source) } - let (part_mani, source, template, sub_prelude) = match input { - Input::File(_, _, content) => { + let source_in_package = package_path.as_ref().join(script_name); + let (part_mani, source_path, source, template, sub_prelude) = match input { + Input::File(_, path, content) => { assert_eq!(prelude_items.len(), 0); - let (content, shebang_used) = strip_shebang(content); + let content = strip_shebang(content); let (manifest, source) = find_embedded_manifest(content).unwrap_or((Manifest::Toml(""), content)); - let source = if contains_main_method(source) { - if shebang_used { - format!("//\n{}", source) - } else { - source.to_string() - } + if contains_main_method(content) { + (manifest, path.clone(), source.to_string(), None, false) } else { - format!("fn main() -> Result<(), Box> {{ {{\n{} }}\n Ok(())\n}}", source) - }; - (manifest, source, consts::FILE_TEMPLATE, false) + ( + manifest, + source_in_package, + content.to_string(), + Some(consts::FILE_NO_MAIN_TEMPLATE), + false, + ) + } } Input::Expr(content) => ( Manifest::Toml(""), + source_in_package, content.to_string(), - consts::EXPR_TEMPLATE, + Some(consts::EXPR_TEMPLATE), true, ), Input::Loop(content, count) => ( Manifest::Toml(""), + source_in_package, content.to_string(), - if *count { + Some(if *count { consts::LOOP_COUNT_TEMPLATE } else { consts::LOOP_TEMPLATE - }, + }), true, ), }; @@ -83,13 +89,23 @@ pub fn split_input( subs.insert(consts::SCRIPT_PRELUDE_SUB, &prelude_str[..]); } - let source = templates::expand(template, &subs)?; + let source = template + .map(|template| templates::expand(template, &subs)) + .transpose()?; let part_mani = part_mani.into_toml()?; info!("part_mani: {:?}", part_mani); info!("source: {:?}", source); + let source_path_from_package = if template.is_some() { + script_name + } else { + source_path + .to_str() + .ok_or_else(|| format!("Unable to stringify {source_path:?}"))? + }; + // It's-a mergin' time! - let def_mani = default_manifest(input, bin_name, script_name, toolchain); + let def_mani = default_manifest(input, bin_name, source_path_from_package, toolchain); let dep_mani = deps_manifest(deps)?; let mani = merge_manifest(def_mani, part_mani)?; @@ -101,7 +117,7 @@ pub fn split_input( let mani_str = format!("{}", mani); info!("manifest: {}", mani_str); - Ok((mani_str, source)) + Ok((mani_str, source_path, source)) } #[cfg(test)] @@ -118,18 +134,27 @@ fn test_split_input() { let toolchain = None; macro_rules! si { ($i:expr) => { - split_input(&$i, &[], &[], &bin_name, &script_name, toolchain.clone()).ok() + split_input( + &$i, + &[], + &[], + "/package", + &bin_name, + &script_name, + toolchain.clone(), + ) + .ok() }; } let f = |c: &str| { - let dummy_path: ::std::path::PathBuf = "main.rs".into(); + let dummy_path: ::std::path::PathBuf = "/dummy/main.rs".into(); Input::File("n".to_string(), dummy_path, c.to_string()) }; macro_rules! r { - ($m:expr, $r:expr) => { - Some(($m.into(), $r.into())) + ($m:expr, $p:expr, $r:expr) => { + Some(($m.into(), $p.into(), $r.into())) }; } @@ -140,7 +165,7 @@ fn test_split_input() { "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] @@ -151,7 +176,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#"fn main() {}"# + "/dummy/main.rs", + None ) ); @@ -163,7 +189,7 @@ fn main() {}"#)), "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] @@ -174,8 +200,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#"// -fn main() {}"# + "/dummy/main.rs", + None ) ); @@ -187,7 +213,7 @@ fn main() {}"#)), "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] @@ -198,8 +224,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#"#[thingy] -fn main() {}"# + "/dummy/main.rs", + None ) ); @@ -208,6 +234,7 @@ fn main() {}"# &f(r#"fn main() {}"#), &[], &[], + "", &bin_name, "main.rs", Some("stable".to_string()) @@ -218,7 +245,7 @@ fn main() {}"# "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] @@ -232,7 +259,8 @@ version = "0.1.0" toolchain = "stable""#, STRIP_SECTION ), - r#"fn main() {}"# + "/dummy/main.rs", + None ) ); @@ -247,7 +275,7 @@ fn main() {} "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] @@ -258,10 +286,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#" ---- -fn main() {} -"# + "/dummy/main.rs", + None ) ); @@ -276,7 +302,7 @@ fn main() {} "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] @@ -287,11 +313,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#"[dependencies] -time="0.1.25" ---- -fn main() {} -"# + "/dummy/main.rs", + None ) ); @@ -305,7 +328,7 @@ fn main() {} "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] time = "0.1.25" @@ -317,10 +340,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#" -// Cargo-Deps: time="0.1.25" -fn main() {} -"# + "/dummy/main.rs", + None ) ); @@ -334,7 +355,7 @@ fn main() {} "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] libc = "0.2.5" @@ -347,10 +368,8 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#" -// Cargo-Deps: time="0.1.25", libc="0.2.5" -fn main() {} -"# + "/dummy/main.rs", + None ) ); @@ -371,7 +390,7 @@ fn main() {} "{}{}", r#"[[bin]] name = "binary-name" -path = "main.rs" +path = "/dummy/main.rs" [dependencies] time = "0.1.25" @@ -383,17 +402,40 @@ name = "n" version = "0.1.0""#, STRIP_SECTION ), - r#" -/*! -Here is a manifest: + "/dummy/main.rs", + None + ) + ); + + assert_eq!( + si!(f(r#"#!/usr/bin/env rust-script +println!("Hello")"#)), + r!( + format!( + "{}{}", + r#"[[bin]] +name = "binary-name" +path = "main.rs" -```cargo [dependencies] -time = "0.1.25" -``` -*/ -fn main() {} + +[package] +authors = ["Anonymous"] +edition = "2021" +name = "n" +version = "0.1.0""#, + STRIP_SECTION + ), + "/package/main.rs", + Some( + r#" +fn main() -> Result<(), Box> { + {println!("Hello")} + Ok(()) +} "# + .to_string() + ) ) ); } @@ -401,11 +443,11 @@ fn main() {} /** Returns a slice of the input string with the leading shebang, if there is one, omitted. */ -fn strip_shebang(s: &str) -> (&str, bool) { +fn strip_shebang(s: &str) -> &str { let re_shebang: Regex = Regex::new(r"^#![^\[].*?(\r\n|\n)").unwrap(); match re_shebang.find(s) { - Some(m) => (&s[m.end()..], true), - None => (s, false), + Some(m) => &s[m.end()..], + None => s, } } @@ -1096,7 +1138,7 @@ Generates a default Cargo manifest for the given input. fn default_manifest( input: &Input, bin_name: &str, - script_name: &str, + bin_source_path: &str, toolchain: Option, ) -> toml::value::Table { let mut package_map = toml::map::Map::new(); @@ -1144,9 +1186,10 @@ fn default_manifest( "name".to_string(), toml::value::Value::String(bin_name.to_string()), ); + bin_map.insert( "path".to_string(), - toml::value::Value::String(script_name.to_string()), + toml::value::Value::String(bin_source_path.to_string()), ); let mut mani_map = toml::map::Map::new();