use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
use std::rc::Rc;
use std::str::FromStr;
use anyhow::anyhow;
use camino::{Utf8Path, Utf8PathBuf};
use ini_merge::filter::FilterAction;
use ini_merge::filter::FilterActions;
use ini_merge::filter::FilterActionsBuilder;
use ini_merge::mutations::transforms;
use ini_merge::mutations::Action;
use ini_merge::mutations::Mutations;
use ini_merge::mutations::MutationsBuilder;
use ini_merge::mutations::SectionAction;
use winnow::Parser;
use crate::transforms::Transform;
use self::parser::Directive;
use self::parser::Matcher;
mod parser;
#[derive(Debug)]
pub(crate) enum Source {
Path(Utf8PathBuf),
Auto,
}
#[derive(Debug)]
pub(crate) struct Config<ActionType>
where
ActionType: Debug,
{
pub(crate) source: Source,
pub(crate) mutations: ActionType,
}
impl<ActionType> Config<ActionType>
where
ActionType: Debug,
{
pub(crate) fn source_path(&self, script_path: &Utf8Path) -> anyhow::Result<Cow<'_, Utf8Path>> {
match self.source {
Source::Path(ref p) => Ok(Cow::Borrowed(p)),
Source::Auto => {
let script_name = script_path
.file_name()
.ok_or_else(|| anyhow!("Failed to extract filename from {script_path:?}"))?;
Ok(Cow::Owned(
script_path
.with_file_name(script_name.strip_prefix("modify_").unwrap_or(script_name))
.with_extension("src.ini"),
))
}
}
}
}
fn make_transformer(
transform: &str,
args: &HashMap<String, String>,
) -> anyhow::Result<Rc<dyn transforms::Transformer>> {
Transform::from_str(transform)
.map_err(|err| anyhow!("Invalid transform specified: {}: {}", transform, err))?
.construct(args)
}
pub(crate) fn parse_for_merge(src: &str) -> anyhow::Result<Config<Mutations>> {
let result = parser::parse_config
.parse(src)
.map_err(|e| anyhow::format_err!("{e}"))?;
let mut source = None;
let mut builder = MutationsBuilder::new();
for directive in result {
match directive {
Directive::WS => (),
Directive::AddRemove(_) | Directive::AddHide(_) => (),
Directive::Source(src) => {
if source.is_some() {
return Err(anyhow!("Duplicate source directives not allowed!"));
}
source = Some(Source::Path(src.into()));
}
Directive::SourceAuto => {
if source.is_some() {
return Err(anyhow!("Duplicate source directives not allowed!"));
}
source = Some(Source::Auto);
}
Directive::Ignore(Matcher::Section(section)) => {
builder.add_section_action(section, SectionAction::Ignore);
}
Directive::Ignore(matcher) => {
add_merge_action(&mut builder, matcher, Action::Ignore);
}
Directive::Transform(matcher, transform, args) => {
let t = make_transformer(&transform, &args)?;
add_merge_action(&mut builder, matcher, Action::Transform(t));
}
Directive::Set {
section,
key,
value,
separator,
} => {
builder.add_setter(
section,
key,
value,
separator.unwrap_or_else(|| " = ".to_string()),
);
}
Directive::Remove(Matcher::Section(section)) => {
builder.add_section_action(section, SectionAction::Delete);
}
Directive::Remove(matcher) => {
add_merge_action(&mut builder, matcher, Action::Delete);
}
}
}
Ok(Config {
source: source.ok_or(anyhow!("No source directive found"))?,
mutations: builder.build()?,
})
}
pub(crate) fn parse_for_add(src: &str) -> Result<Config<FilterActions>, anyhow::Error> {
let result = parser::parse_config
.parse(src)
.map_err(|e| anyhow::format_err!("{e}"))?;
let mut source = None;
let mut builder = FilterActionsBuilder::new();
for directive in result {
match directive {
Directive::WS => (),
Directive::AddHide(matcher) => {
add_filter_action(&mut builder, matcher, FilterAction::Replace("HIDDEN"));
}
Directive::AddRemove(matcher) | Directive::Ignore(matcher) => {
add_filter_action(&mut builder, matcher, FilterAction::Remove);
}
Directive::Source(src) => {
if source.is_some() {
return Err(anyhow!("Duplicate source directives not allowed!"));
}
source = Some(Source::Path(src.into()));
}
Directive::SourceAuto => {
if source.is_some() {
return Err(anyhow!("Duplicate source directives not allowed!"));
}
source = Some(Source::Auto);
}
Directive::Set { .. } => (),
Directive::Transform(_, _, _) => (),
Directive::Remove(_) => (),
}
}
Ok(Config {
source: source.ok_or(anyhow!("No source directive found"))?,
mutations: builder.build()?,
})
}
fn add_merge_action(builder: &mut MutationsBuilder, matcher: Matcher, action: Action) {
match matcher {
Matcher::Section(_) => panic!("Section match not valid in add_merge_action()"),
Matcher::Literal(section, key) => {
builder.add_literal_action(section, key, action);
}
Matcher::Regex(section, key) => {
builder.add_regex_action(section, key, action);
}
}
}
fn add_filter_action(builder: &mut FilterActionsBuilder, matcher: Matcher, action: FilterAction) {
match matcher {
Matcher::Section(section) => {
builder.add_section_action(section, action);
}
Matcher::Literal(section, key) => {
builder.add_literal_action(section, key, action);
}
Matcher::Regex(section, key) => {
builder.add_regex_action(section, key, action);
}
}
}