1use std::convert::TryFrom;
23use std::path::PathBuf;
24
25#[cfg(target_os = "linux")]
26use dotfiles_actions::apt::action::AptAction;
27#[cfg(any(target_os = "linux", target_os = "macos"))]
28use dotfiles_actions::brew::action::BrewAction;
29use dotfiles_actions::create::action::NativeCreateAction;
30use dotfiles_actions::exec::action::ExecAction;
31#[cfg(unix)]
32use dotfiles_actions::link::action::NativeLinkAction;
33
34use dotfiles_core::error::{DotfilesError, ErrorType};
35use dotfiles_core::yaml_util::map_yaml_array;
36use dotfiles_core::{Action, Settings};
37use strict_yaml_rust::StrictYaml;
38
39use crate::context::Context;
40
41#[derive(Clone)]
42pub enum KnownDirective {
43 #[cfg(target_os = "linux")]
44 Apt,
45 #[cfg(any(target_os = "linux", target_os = "macos"))]
46 Brew,
47 Create,
48 Exec,
49 #[cfg(unix)]
50 Link,
51 Subconfig,
52}
53
54pub enum KnownAction {
55 #[cfg(target_os = "linux")]
56 Apt(AptAction),
57 #[cfg(any(target_os = "linux", target_os = "macos"))]
58 Brew(BrewAction),
59 Create(NativeCreateAction),
60 Exec(ExecAction),
61 #[cfg(unix)]
62 Link(NativeLinkAction),
63 Subconfig(Context),
64}
65
66#[cfg(target_os = "linux")]
67impl From<AptAction> for KnownAction {
68 fn from(value: AptAction) -> Self {
69 KnownAction::Apt(value)
70 }
71}
72
73#[cfg(any(target_os = "linux", target_os = "macos"))]
74impl From<BrewAction> for KnownAction {
75 fn from(value: BrewAction) -> Self {
76 KnownAction::Brew(value)
77 }
78}
79
80impl From<NativeCreateAction> for KnownAction {
81 fn from(value: NativeCreateAction) -> Self {
82 KnownAction::Create(value)
83 }
84}
85
86impl From<ExecAction> for KnownAction {
87 fn from(value: ExecAction) -> Self {
88 KnownAction::Exec(value)
89 }
90}
91
92#[cfg(unix)]
93impl From<NativeLinkAction> for KnownAction {
94 fn from(value: NativeLinkAction) -> Self {
95 KnownAction::Link(value)
96 }
97}
98
99impl From<Context> for KnownAction {
100 fn from(value: Context) -> Self {
101 KnownAction::Subconfig(value)
102 }
103}
104
105impl KnownAction {
106 pub fn execute(self) -> Result<(), DotfilesError> {
107 match self {
108 #[cfg(target_os = "linux")]
109 KnownAction::Apt(action) => action.check_conditions_and_execute().map_err(|mut err| {
110 err.add_message_prefix("Executing apt action".into());
111 err
112 }),
113 #[cfg(any(target_os = "linux", target_os = "macos"))]
114 KnownAction::Brew(action) => action.check_conditions_and_execute().map_err(|mut err| {
115 err.add_message_prefix("Executing brew action".into());
116 err
117 }),
118 KnownAction::Create(action) => action.check_conditions_and_execute().map_err(|mut err| {
119 err.add_message_prefix("Executing create action".into());
120 err
121 }),
122 KnownAction::Exec(action) => action.check_conditions_and_execute().map_err(|mut err| {
123 err.add_message_prefix("Executing exec action".into());
124 err
125 }),
126 #[cfg(unix)]
127 KnownAction::Link(action) => action.check_conditions_and_execute().map_err(|mut err| {
128 err.add_message_prefix("Executing link action".into());
129 err
130 }),
131 KnownAction::Subconfig(subcontext) => Context::run_actions(subcontext),
132 }
133 }
134}
135
136impl KnownDirective {
137 pub fn name(&self) -> &'static str {
138 match self {
139 #[cfg(target_os = "linux")]
140 KnownDirective::Apt => "apt",
141 #[cfg(any(target_os = "linux", target_os = "macos"))]
142 KnownDirective::Brew => "brew",
143 KnownDirective::Create => "create",
144 KnownDirective::Exec => "exec",
145 #[cfg(unix)]
146 KnownDirective::Link => "link",
147 KnownDirective::Subconfig => "subconfig",
148 }
149 }
150
151 pub fn default_settings(&self) -> Settings {
152 match self {
153 #[cfg(target_os = "linux")]
154 KnownDirective::Apt => dotfiles_actions::apt::action::default_settings(),
155 #[cfg(any(target_os = "linux", target_os = "macos"))]
156 KnownDirective::Brew => dotfiles_actions::brew::action::default_settings(),
157 KnownDirective::Create => dotfiles_actions::create::action::default_settings(),
158 KnownDirective::Exec => dotfiles_actions::exec::action::default_settings(),
159 #[cfg(unix)]
160 KnownDirective::Link => dotfiles_actions::link::action::default_settings(),
161 KnownDirective::Subconfig => Settings::new(),
162 }
163 }
164
165 pub fn parse_context_defaults(
166 &self,
167 defaults: &strict_yaml_rust::StrictYaml,
168 ) -> Result<(String, Settings), DotfilesError> {
169 Ok((
170 self.name().to_owned(),
171 dotfiles_core::yaml_util::parse_context_defaults(
172 self.name(),
173 &self.default_settings(),
174 defaults,
175 )?,
176 ))
177 }
178
179 pub fn parse_action_list(
180 directive: KnownDirective,
181 context_settings: &Settings,
182 actions: &strict_yaml_rust::StrictYaml,
183 context: &Context,
184 ) -> Result<Vec<KnownAction>, DotfilesError> {
185 let file = context.file();
186 let current_dir = file.parent().ok_or_else(||
187 DotfilesError::from(
188 format!(
189 "{} doesn't seem to have a parent dir to use as a current directory to parse actions, this makes no sense and should never happen",
190 file.to_str().unwrap()) ,
191 ErrorType::CoreError))?;
192 match directive {
193 #[cfg(target_os = "linux")]
194 KnownDirective::Apt => dotfiles_actions::apt::action::parse_action_list(context_settings, actions, current_dir)
195 .map(|list| list.into_iter().map(KnownAction::from).collect()),
196 #[cfg(any(target_os = "linux", target_os = "macos"))]
197 KnownDirective::Brew => dotfiles_actions::brew::action::parse_action_list(context_settings, actions, current_dir)
198 .map(|list| list.into_iter().map(KnownAction::from).collect()),
199 KnownDirective::Create => {
200 dotfiles_core::yaml_util::map_yaml_array(actions, |yaml_item| {
201 dotfiles_actions::create::action::parse_action::<dotfiles_actions::filesystem::OsFileSystem>(
202 dotfiles_actions::filesystem::OsFileSystem::default(),
203 context_settings,
204 yaml_item,
205 current_dir,
206 )
207 })
208 .map(|list| list.into_iter().map(KnownAction::from).collect())
209 }
210 KnownDirective::Exec => {
211 dotfiles_core::yaml_util::map_yaml_array(actions, |yaml_item| {
212 dotfiles_actions::exec::action::parse_action(context_settings, yaml_item, current_dir)
213 })
214 .map(|list| list.into_iter().map(KnownAction::from).collect())
215 }
216 #[cfg(unix)]
217 KnownDirective::Link => {
218 dotfiles_core::yaml_util::map_yaml_array(actions, |yaml_item| {
219 dotfiles_actions::link::action::parse_action::<dotfiles_actions::filesystem::OsFileSystem>(
220 dotfiles_actions::filesystem::OsFileSystem::default(),
221 context_settings,
222 yaml_item,
223 current_dir,
224 )
225 })
226 .map(|list| list.into_iter().map(KnownAction::from).collect())
227 }
228 KnownDirective::Subconfig => map_yaml_array(actions, |file_yaml| {
229 file_yaml
230 .clone()
231 .into_string()
232 .ok_or(DotfilesError::from_wrong_yaml(
233 "Subconfig: Expected a file name".to_owned(),
234 actions.clone(),
235 StrictYaml::String("".into()),
236 ))
237 .and_then(|filename| {
238 let path = PathBuf::from(&filename);
239 let mut subcontext = context.subcontext(&path)?;
240 subcontext.parse_file()?;
241 Ok(KnownAction::from(subcontext))
242 })
243 }),
244 }
245 }
246}
247
248impl TryFrom<&str> for KnownDirective {
249 type Error = DotfilesError;
250
251 fn try_from(value: &str) -> Result<Self, Self::Error> {
252 match value {
253 #[cfg(target_os = "linux")]
254 "apt" => Ok(KnownDirective::Apt),
255 #[cfg(any(target_os = "linux", target_os = "macos"))]
256 "brew" => Ok(KnownDirective::Brew),
257 "create" => Ok(KnownDirective::Create),
258 "exec" => Ok(KnownDirective::Exec),
259 "link" => Ok(KnownDirective::Link),
260 "subconfig" => Ok(KnownDirective::Subconfig),
261 _ => Err(DotfilesError::from(
262 format!("Configuration refers to unknown action type `{value}`"),
263 ErrorType::InconsistentConfigurationError,
264 )),
265 }
266 }
267}