1extern crate strict_yaml_rust;
25
26use crate::filesystem::FileSystemDirective;
27use crate::link::action::LinkAction;
28use dotfiles_core::action::ActionParser;
29use dotfiles_core::action::SKIP_IN_CI_SETTING;
30use dotfiles_core::directive::Directive;
31use dotfiles_core::directive::DirectiveData;
32use dotfiles_core::directive::HasDirectiveData;
33use dotfiles_core::error::DotfilesError;
34use dotfiles_core::error::ErrorType;
35use dotfiles_core::settings::initialize_settings_object;
36use dotfiles_core::settings::Setting;
37use dotfiles_core::settings::Settings;
38use dotfiles_core::yaml_util::*;
39use dotfiles_core_macros::Directive;
40use filesystem::FakeFileSystem;
41use filesystem::FileSystem;
42use filesystem::OsFileSystem;
43use filesystem::UnixFileSystem;
44use std::marker::PhantomData;
45use std::path::Path;
46use strict_yaml_rust::StrictYaml;
47
48pub const DIRECTIVE_NAME: &str = "link";
50pub const PATH_SETTING: &str = "path";
52pub const TARGET_SETTING: &str = "target";
54pub const FORCE_SETTING: &str = "force";
56pub const RELINK_SETTING: &str = "relink";
59pub const CREATE_PARENT_DIRS_SETTING: &str = "create_parent_dirs";
61pub const IGNORE_MISSING_TARGET_SETTING: &str = "ignore_missing_target";
63pub const RESOLVE_SYMLINK_TARGET_SETTING: &str = "resolve_symlink_target";
65
66pub fn init_directive_data() -> DirectiveData {
68 DirectiveData::from(
69 DIRECTIVE_NAME.into(),
70 initialize_settings_object(&[
71 (FORCE_SETTING.to_owned(), Setting::Boolean(false)),
72 (RELINK_SETTING.to_owned(), Setting::Boolean(false)),
73 (
74 CREATE_PARENT_DIRS_SETTING.to_owned(),
75 Setting::Boolean(false),
76 ),
77 (
78 IGNORE_MISSING_TARGET_SETTING.to_owned(),
79 Setting::Boolean(false),
80 ),
81 (
82 RESOLVE_SYMLINK_TARGET_SETTING.to_owned(),
83 Setting::Boolean(false),
84 ),
85 (SKIP_IN_CI_SETTING.to_owned(), Setting::Boolean(false)),
86 ]),
87 )
88}
89
90#[derive(Directive, Clone)]
93pub struct LinkDirective<'a, F: FileSystem + UnixFileSystem + Default> {
94 fs: F,
95 data: DirectiveData,
96 phantom: PhantomData<&'a F>,
97}
98
99pub type NativeLinkDirective<'a> = LinkDirective<'a, OsFileSystem>;
101pub type FakeLinkDirective<'a> = LinkDirective<'a, FakeFileSystem>;
103
104impl<'a, F: FileSystem + UnixFileSystem + Default> Default for LinkDirective<'a, F> {
105 fn default() -> Self {
106 Self {
107 fs: Default::default(),
108 data: init_directive_data(),
109 phantom: Default::default(),
110 }
111 }
112}
113
114impl<'a, F: FileSystem + UnixFileSystem + Default> FileSystemDirective<'a, F>
115 for LinkDirective<'a, F>
116{
117 fn fs(&self) -> &F {
118 &self.fs
119 }
120
121 fn mut_fs(&mut self) -> &mut F {
122 &mut self.fs
123 }
124}
125
126impl<'a, F: FileSystem + UnixFileSystem + Default> LinkDirective<'a, F>
127where
128 LinkDirective<'a, F>: HasDirectiveData<'a> + Directive<'a>,
129{
130 pub fn fs(&self) -> &F {
132 &self.fs
133 }
134
135 fn parse_full_action(
136 &'a self,
137 context_settings: &Settings,
138 yaml: &StrictYaml,
139 current_dir: &Path,
140 ) -> Result<LinkAction<'a, F>, DotfilesError> {
141 let path = get_string_setting_from_yaml_or_context(
142 PATH_SETTING,
143 yaml,
144 context_settings,
145 self.data.defaults(),
146 )?;
147 let target = get_string_setting_from_yaml_or_context(
148 TARGET_SETTING,
149 yaml,
150 context_settings,
151 self.data.defaults(),
152 )?;
153 let action_settings: Result<Settings, DotfilesError> = self
154 .directive_data()
155 .defaults()
156 .iter()
157 .map(|(name, _)| {
158 self
159 .get_setting_from_yaml_hash_or_from_context(name, yaml, context_settings)
160 .map(|setting| (name.to_owned(), setting))
161 })
162 .collect();
163
164 LinkAction::<'a, F>::new(
165 &self.fs,
166 path,
167 target,
168 &action_settings?,
169 self.data.defaults(),
170 current_dir.to_owned(),
171 )
172 }
173
174 pub fn parse_shortened_action(
176 &'a self,
177 context_settings: &Settings,
178 yaml: &StrictYaml,
179 current_dir: &Path,
180 ) -> Result<LinkAction<'a, F>, DotfilesError> {
181 if let StrictYaml::Hash(hash) = yaml {
182 match hash.len() {
183 1 => {
184 if let (StrictYaml::String(path), StrictYaml::String(target)) = hash.front().unwrap() {
185 LinkAction::<'a, F>::new(
186 &self.fs,
187 path.clone(),
188 target.clone(),
189 context_settings,
190 self.data.defaults(),
191 current_dir.to_owned()
192 )
193 } else {
194 Err(DotfilesError::from_wrong_yaml(
195 "StrictYaml passed to configure a short Link action is not a hash of string to string, cant parse".into(),
196 yaml.to_owned(), StrictYaml::Hash(Default::default())))
197 }
198 }
199
200 x => Err(DotfilesError::from(
201 format!(
202 "StrictYaml passed to configure a short Link action is a hash with {x} values, must be just 1",),
203 ErrorType::InconsistentConfigurationError,
204 )),
205 }
206 } else {
207 Err(DotfilesError::from_wrong_yaml(
208 "StrictYaml passed to configure a Link action is not a Hash".into(),
209 yaml.to_owned(),
210 StrictYaml::Hash(Default::default()),
211 ))
212 }
213 }
214}
215
216impl<'a, F: FileSystem + UnixFileSystem + Default> ActionParser<'a> for LinkDirective<'a, F> {
217 type ActionType = LinkAction<'a, F>;
218
219 fn parse_action(
220 &'a self,
221 settings: &Settings,
222 yaml: &StrictYaml,
223 current_directory: &Path,
224 ) -> Result<LinkAction<'a, F>, DotfilesError> {
225 self
226 .parse_shortened_action(settings, yaml, current_directory)
227 .or_else(|_| self.parse_full_action(settings, yaml, current_directory))
228 }
229}