archetect_core/actions/
exec.rs1use std::collections::hash_map::RandomState;
2use std::path::Path;
3use std::process::Command;
4
5use linked_hash_map::LinkedHashMap;
6use log::{debug, warn};
7
8use crate::actions::Action;
9use crate::config::VariableInfo;
10use crate::rules::RulesContext;
11use crate::{Archetect, ArchetectError, Archetype};
12use crate::vendor::tera::Context;
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
15pub struct ExecAction {
16 command: String,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 args: Option<Vec<String>>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 env: Option<LinkedHashMap<String, String>>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 cwd: Option<String>,
23}
24
25impl ExecAction {
26 pub fn new<C: Into<String>>(command: C) -> ExecAction {
27 ExecAction {
28 command: command.into(),
29 args: None,
30 env: None,
31 cwd: None,
32 }
33 }
34
35 pub fn args(&self) -> Option<&Vec<String>> {
36 self.args.as_ref()
37 }
38
39 pub fn with_arg<A: Into<String>>(mut self, arg: A) -> ExecAction {
40 self.add_arg(arg);
41 self
42 }
43
44 pub fn add_arg<A: Into<String>>(&mut self, arg: A) {
45 let args = self.args.get_or_insert_with(|| Default::default());
46 args.push(arg.into());
47 }
48
49 pub fn env(&self) -> Option<&LinkedHashMap<String, String>> {
50 self.env.as_ref()
51 }
52
53 pub fn with_environment_variable<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> ExecAction {
54 self.add_environment_variable(key, value);
55 self
56 }
57
58 pub fn add_environment_variable<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) {
59 let env = self.env.get_or_insert_with(|| Default::default());
60 env.insert(key.into(), value.into());
61 }
62
63 pub fn cwd(&self) -> Option<&String> {
64 self.cwd.as_ref()
65 }
66
67 pub fn with_working_directory<D: Into<String>>(mut self, directory: D) -> ExecAction {
68 self.set_working_directory(directory);
69 self
70 }
71
72 pub fn set_working_directory<D: Into<String>>(&mut self, directory: D) {
73 self.cwd = Some(directory.into());
74 }
75}
76
77impl Action for ExecAction {
78 fn execute<D: AsRef<Path>>(
79 &self,
80 archetect: &mut Archetect,
81 _archetype: &Archetype,
82 destination: D,
83 _rules_context: &mut RulesContext,
84 _answers: &LinkedHashMap<String, VariableInfo, RandomState>,
85 context: &mut Context,
86 ) -> Result<(), ArchetectError> {
87 let mut command = Command::new(&self.command);
88
89 if let Some(args) = self.args() {
90 for arg in args {
91 command.arg(archetect.render_string(arg, context)?);
92 }
93 }
94
95 if let Some(env) = self.env() {
96 for (key, value) in env {
97 command.env(
98 archetect.render_string(key, context)?,
99 archetect.render_string(value, context)?,
100 );
101 }
102 }
103
104 if let Some(cwd) = &self.cwd {
105 if let Ok(cwd) = shellexpand::full(cwd) {
106 let cwd = Path::new(cwd.as_ref());
107 if cwd.is_relative() {
108 command.current_dir(
109 destination
110 .as_ref()
111 .join(archetect.render_string(cwd.display().to_string().as_str(), context)?),
112 );
113 } else {
114 command.current_dir(archetect.render_string(cwd.display().to_string().as_str(), context)?);
115 }
116 }
117 } else {
118 command.current_dir(destination);
119 }
120
121 debug!("[exec] Executing: {:?}", command);
122 match command.status() {
123 Ok(status) => {
124 debug!("[exec] Status: {}", status.code().unwrap());
125 }
126 Err(error) => {
127 warn!("[exec] Error: {}", error);
128 }
129 }
130
131 Ok(())
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::actions::exec::ExecAction;
138 use linked_hash_map::LinkedHashMap;
139 use serde_yaml;
140
141 #[test]
142 fn test_serialize() {
143 let mut env = LinkedHashMap::new();
144 env.insert("M2_HOME".to_owned(), "~/.m2".to_owned());
145 env.insert("MAVEN_HOME".to_owned(), "/usr/bin".to_owned());
146
147 let mut foo = LinkedHashMap::new();
148 foo.insert("exmple".to_owned(), ());
149 let action = ExecAction {
150 command: "mvn".to_string(),
151 args: Some(vec!["install".to_owned()]),
152 env: Some(env),
153 cwd: None,
154 };
155
156 println!("{}", serde_yaml::to_string(&action).unwrap());
157 }
158}