extern crate strict_yaml_rust;
use crate::filesystem::FileSystemDirective;
use crate::link::action::LinkAction;
use dotfiles_core::action::ActionParser;
use dotfiles_core::action::SKIP_IN_CI_SETTING;
use dotfiles_core::directive::Directive;
use dotfiles_core::directive::DirectiveData;
use dotfiles_core::directive::HasDirectiveData;
use dotfiles_core::error::DotfilesError;
use dotfiles_core::error::ErrorType;
use dotfiles_core::settings::initialize_settings_object;
use dotfiles_core::settings::Setting;
use dotfiles_core::settings::Settings;
use dotfiles_core::yaml_util::*;
use dotfiles_core_macros::Directive;
use filesystem::FakeFileSystem;
use filesystem::FileSystem;
use filesystem::OsFileSystem;
use filesystem::UnixFileSystem;
use std::marker::PhantomData;
use std::path::Path;
use strict_yaml_rust::StrictYaml;
pub const DIRECTIVE_NAME: &str = "link";
pub const PATH_SETTING: &str = "path";
pub const TARGET_SETTING: &str = "target";
pub const FORCE_SETTING: &str = "force";
pub const RELINK_SETTING: &str = "relink";
pub const CREATE_PARENT_DIRS_SETTING: &str = "create_parent_dirs";
pub const IGNORE_MISSING_TARGET_SETTING: &str = "ignore_missing_target";
pub const RESOLVE_SYMLINK_TARGET_SETTING: &str = "resolve_symlink_target";
pub fn init_directive_data() -> DirectiveData {
DirectiveData::from(
DIRECTIVE_NAME.into(),
initialize_settings_object(&[
(FORCE_SETTING.to_owned(), Setting::Boolean(false)),
(RELINK_SETTING.to_owned(), Setting::Boolean(false)),
(
CREATE_PARENT_DIRS_SETTING.to_owned(),
Setting::Boolean(false),
),
(
IGNORE_MISSING_TARGET_SETTING.to_owned(),
Setting::Boolean(false),
),
(
RESOLVE_SYMLINK_TARGET_SETTING.to_owned(),
Setting::Boolean(false),
),
(SKIP_IN_CI_SETTING.to_owned(), Setting::Boolean(false)),
]),
)
}
#[derive(Directive, Clone)]
pub struct LinkDirective<'a, F: FileSystem + UnixFileSystem + Default> {
fs: F,
data: DirectiveData,
phantom: PhantomData<&'a F>,
}
pub type NativeLinkDirective<'a> = LinkDirective<'a, OsFileSystem>;
pub type FakeLinkDirective<'a> = LinkDirective<'a, FakeFileSystem>;
impl<'a, F: FileSystem + UnixFileSystem + Default> Default for LinkDirective<'a, F> {
fn default() -> Self {
Self {
fs: Default::default(),
data: init_directive_data(),
phantom: Default::default(),
}
}
}
impl<'a, F: FileSystem + UnixFileSystem + Default> FileSystemDirective<'a, F>
for LinkDirective<'a, F>
{
fn fs(&self) -> &F {
&self.fs
}
fn mut_fs(&mut self) -> &mut F {
&mut self.fs
}
}
impl<'a, F: FileSystem + UnixFileSystem + Default> LinkDirective<'a, F>
where
LinkDirective<'a, F>: HasDirectiveData<'a> + Directive<'a>,
{
pub fn fs(&self) -> &F {
&self.fs
}
fn parse_full_action(
&'a self,
context_settings: &Settings,
yaml: &StrictYaml,
current_dir: &Path,
) -> Result<LinkAction<'a, F>, DotfilesError> {
let path = get_string_setting_from_yaml_or_context(
PATH_SETTING,
yaml,
context_settings,
self.data.defaults(),
)?;
let target = get_string_setting_from_yaml_or_context(
TARGET_SETTING,
yaml,
context_settings,
self.data.defaults(),
)?;
let action_settings: Result<Settings, DotfilesError> = self
.directive_data()
.defaults()
.iter()
.map(|(name, _)| {
self
.get_setting_from_yaml_hash_or_from_context(name, yaml, context_settings)
.map(|setting| (name.to_owned(), setting))
})
.collect();
LinkAction::<'a, F>::new(
&self.fs,
path,
target,
&action_settings?,
self.data.defaults(),
current_dir.to_owned(),
)
}
pub fn parse_shortened_action(
&'a self,
context_settings: &Settings,
yaml: &StrictYaml,
current_dir: &Path,
) -> Result<LinkAction<'a, F>, DotfilesError> {
if let StrictYaml::Hash(hash) = yaml {
match hash.len() {
1 => {
if let (StrictYaml::String(path), StrictYaml::String(target)) = hash.front().unwrap() {
LinkAction::<'a, F>::new(
&self.fs,
path.clone(),
target.clone(),
context_settings,
self.data.defaults(),
current_dir.to_owned()
)
} else {
Err(DotfilesError::from_wrong_yaml(
"StrictYaml passed to configure a short Link action is not a hash of string to string, cant parse".into(),
yaml.to_owned(), StrictYaml::Hash(Default::default())))
}
}
x => Err(DotfilesError::from(
format!(
"StrictYaml passed to configure a short Link action is a hash with {x} values, must be just 1",),
ErrorType::InconsistentConfigurationError,
)),
}
} else {
Err(DotfilesError::from_wrong_yaml(
"StrictYaml passed to configure a Link action is not a Hash".into(),
yaml.to_owned(),
StrictYaml::Hash(Default::default()),
))
}
}
}
impl<'a, F: FileSystem + UnixFileSystem + Default> ActionParser<'a> for LinkDirective<'a, F> {
type ActionType = LinkAction<'a, F>;
fn parse_action(
&'a self,
settings: &Settings,
yaml: &StrictYaml,
current_directory: &Path,
) -> Result<LinkAction<'a, F>, DotfilesError> {
self
.parse_shortened_action(settings, yaml, current_directory)
.or_else(|_| self.parse_full_action(settings, yaml, current_directory))
}
}