archetect_core/actions/
render.rs1use std::path::Path;
2
3use linked_hash_map::LinkedHashMap;
4
5use crate::actions::{set, Action};
6use crate::config::AnswerInfo;
7use crate::rules::RulesContext;
8use crate::vendor::tera::Context;
9use crate::{Archetect, ArchetectError, Archetype};
10use std::fs;
11
12#[derive(Debug, Serialize, Deserialize, Clone)]
13pub enum RenderAction {
14 #[serde(rename = "directory")]
15 Directory(DirectoryOptions),
16 #[serde(rename = "archetype")]
17 Archetype(ArchetypeOptions),
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
21pub struct DirectoryOptions {
22 #[serde(skip_serializing_if = "Option::is_none")]
23 destination: Option<String>,
24 source: String,
25}
26
27impl DirectoryOptions {
28 pub fn new<S: Into<String>>(source: S) -> DirectoryOptions {
29 DirectoryOptions {
30 source: source.into(),
31 destination: None,
32 }
33 }
34
35 pub fn with_destination<D: Into<String>>(mut self, destination: D) -> DirectoryOptions {
36 self.destination = Some(destination.into());
37 self
38 }
39}
40
41#[derive(Debug, Serialize, Deserialize, Clone)]
42pub struct ArchetypeOptions {
43 #[serde(skip_serializing_if = "Option::is_none", rename = "inherit-answers", alias = "answers-include")]
44 answers_include: Option<Vec<String>>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 answers: Option<LinkedHashMap<String, AnswerInfo>>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 destination: Option<String>,
49 source: String,
50}
51
52impl ArchetypeOptions {
53 pub fn new<S: Into<String>>(source: S) -> ArchetypeOptions {
54 ArchetypeOptions {
55 answers_include: None,
56 answers: None,
57 source: source.into(),
58 destination: None,
59 }
60 }
61
62 pub fn with_destination<D: Into<String>>(mut self, destination: D) -> ArchetypeOptions {
63 self.destination = Some(destination.into());
64 self
65 }
66
67 pub fn with_inherited_answer(mut self, key: String) -> ArchetypeOptions {
68 self.answers_include.get_or_insert_with(|| Vec::new()).push(key);
69 self
70 }
71
72 pub fn with_answer(mut self, key: String, value: AnswerInfo) -> ArchetypeOptions {
73 self.answers.get_or_insert_with(|| LinkedHashMap::new()).insert(key, value);
74 self
75 }
76}
77
78impl Action for RenderAction {
79 fn execute<D: AsRef<Path>>(
80 &self,
81 archetect: &mut Archetect,
82 archetype: &Archetype,
83 destination: D,
84 rules_context: &mut RulesContext,
85 _answers: &LinkedHashMap<String, AnswerInfo>,
86 context: &mut Context,
87 ) -> Result<(), ArchetectError> {
88 match self {
89 RenderAction::Directory(options) => {
90 let source = archetype.source().directory().join(&options.source);
91 let destination = if let Some(dest) = &options.destination {
92 if let Ok(result) = shellexpand::full(dest) {
93 use log::debug;
94 debug!("Archetype ShellExpand Dest: {}", result);
95 }
96 destination.as_ref().join(archetect.render_string(dest, context)?)
97 } else {
98 destination.as_ref().to_owned()
99 };
100 fs::create_dir_all(destination.as_path())?;
101 archetect.render_directory(context, source, destination, rules_context)?;
102 }
103
104 RenderAction::Archetype(options) => {
105 let destination = if let Some(dest) = &options.destination {
106 destination.as_ref().join(archetect.render_string(dest, context)?)
107 } else {
108 destination.as_ref().to_owned()
109 };
110 let archetype = archetect.load_archetype(&options.source, Some(archetype.source().clone()))?;
111
112 let mut scoped_answers = LinkedHashMap::new();
113
114 if let Some(answers_include) = &options.answers_include {
115 for identifier in answers_include {
116 if let Some(value) = context.get(identifier) {
117 if let Some(string) = value.as_str() {
118 scoped_answers.insert(identifier.to_owned(), AnswerInfo::with_value(string).build());
119 }
120 }
121 }
122 }
123
124 if let Some(answers) = &options.answers {
128 let rendered_answers = set::render_answers(archetect, answers, context)?;
129 for (key, value) in rendered_answers {
130 scoped_answers.insert(key, value);
131 }
132 };
133
134 archetype.render(archetect, &destination, &scoped_answers)?;
135 }
136 }
137
138 Ok(())
139 }
140}