1use std::ffi::OsStr;
2use std::io::BufRead;
3use std::path::PathBuf;
4use std::process::Command;
5
6use crate::error::{Error, Result};
7use crate::expand::Project;
8use crate::manifest::Name;
9use crate::rustflags;
10use serde_derive::Deserialize;
11
12#[derive(Deserialize)]
13pub struct Metadata {
14 pub target_directory: PathBuf,
15 pub workspace_root: PathBuf,
16}
17
18fn raw_cargo() -> Command {
19 Command::new(option_env!("CARGO").unwrap_or("cargo"))
20}
21
22fn cargo(project: &Project) -> Command {
23 let mut cmd = raw_cargo();
24 cmd.current_dir(&project.dir);
25 cmd.env("CARGO_TARGET_DIR", &project.inner_target_dir);
26 rustflags::set_env(&mut cmd);
27 cmd
28}
29
30pub(crate) fn metadata() -> Result<Metadata> {
31 let output = raw_cargo()
32 .arg("metadata")
33 .arg("--format-version=1")
34 .output()
35 .map_err(Error::Cargo)?;
36
37 serde_json::from_slice(&output.stdout).map_err(Error::CargoMetadata)
38}
39
40pub(crate) fn expand<I, S>(
41 project: &Project,
42 name: &Name,
43 args: &Option<I>,
44) -> Result<(bool, Vec<u8>)>
45where
46 I: IntoIterator<Item = S> + Clone,
47 S: AsRef<OsStr>,
48{
49 let mut cargo = cargo(project);
50 let cargo = cargo
51 .arg("expand")
52 .arg("--bin")
53 .arg(name.as_ref())
54 .arg("--theme")
55 .arg("none");
56
57 if let Some(args) = args {
58 cargo.args(args.clone());
59 }
60
61 let cargo_expand = cargo
62 .output()
63 .map_err(|e| Error::CargoExpandExecution(e.to_string()))?;
64
65 if !cargo_expand.status.success() {
66 return Ok((false, cargo_expand.stderr));
67 }
68
69 Ok((true, cargo_expand.stdout))
70}
71
72pub(crate) fn build_dependencies(project: &Project) -> Result<()> {
77 use std::io::Write;
78
79 let stdout = cargo(project)
80 .arg("expand")
81 .arg("--bin")
82 .arg(project.name.clone())
83 .arg("--theme")
84 .arg("none")
85 .stdout(std::process::Stdio::piped())
86 .spawn()?
87 .stdout
88 .ok_or(Error::CargoFail)?;
89
90 let reader = std::io::BufReader::new(stdout);
91
92 reader
94 .lines()
95 .filter_map(|line| line.ok())
96 .filter(|line| !line.starts_with("fn main() {}"))
97 .filter(|line| !line_should_be_ignored(line))
98 .for_each(|line| {
99 let _ = writeln!(std::io::stdout(), "{}", line);
100 });
101
102 Ok(())
103}
104
105const IGNORED_LINES: [&str; 5] = [
106 "#![feature(prelude_import)]",
107 "#[prelude_import]",
108 "use std::prelude::",
109 "#[macro_use]",
110 "extern crate std;",
111];
112
113fn line_should_be_ignored(line: &str) -> bool {
114 for check in IGNORED_LINES.iter() {
115 if line.starts_with(check) {
116 return true;
117 }
118 }
119
120 false
121}