Skip to content

Commit

Permalink
Changed format of JSON rules file
Browse files Browse the repository at this point in the history
  • Loading branch information
PsychedelicShayna committed Sep 6, 2022
1 parent ce1e0ea commit e2ed7f8
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 94 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fassoc-proxy"
version = "0.1.0"
version = "2.0.0"
edition = "2021"

[dependencies]
Expand Down
38 changes: 19 additions & 19 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ mod logging;
use logging::MAIN_LOGGER;

mod rules;
use rules::{FassocRules, Rule};
use rules::{Command, FassocRules};

use crate::winproc::create_process;
use crate::winproc::invoke_command;

mod winproc;

Expand Down Expand Up @@ -43,25 +43,25 @@ fn read_fassoc_rules(path: String) -> Result<FassocRules, ReadRulesError> {
}

fn subst_arg_placeholders(rules: FassocRules, args: Vec<String>) -> FassocRules {
let mut new_rules: FassocRules = rules;
let mut subst_rules: FassocRules = rules;

for (index, argument) in args.iter().enumerate() {
let placeholder = format!("~~${}", index);

for rule in new_rules.rules.values_mut() {
rule.command = rule.command.replace(&placeholder, argument);
for command in subst_rules.commands.values_mut() {
command.path = command.path.replace(&placeholder, argument);

rule.arguments = rule
command.arguments = command
.arguments
.to_owned()
.map(|arg| arg.replace(&placeholder, argument));

rule.cwd = rule
command.cwd = command
.cwd
.to_owned()
.map(|cwd| cwd.replace(&placeholder, argument));

rule.extras = rule.extras.to_owned().map(|mut extras| {
command.extras = command.extras.to_owned().map(|mut extras| {
extras.title = extras.title.map(|str| str.replace(&placeholder, argument));
extras.desktop = extras
.desktop
Expand All @@ -71,7 +71,7 @@ fn subst_arg_placeholders(rules: FassocRules, args: Vec<String>) -> FassocRules
}
}

new_rules
subst_rules
}

fn main() {
Expand Down Expand Up @@ -102,31 +102,31 @@ fn main() {
}
};

let proxy_rules_path: String = if cli_args.len() >= 3 {
let fassoc_rules_path: String = if cli_args.len() >= 3 {
cli_args[2].clone()
} else {
match env::var("FASSOC_PROXY_RULES") {
match env::var("FASSOC_RULES_PATH") {
Ok(val) => val,
Err(_) => {
log::error!("No argument or environment variable was given that points to the proxy rules file.");
log::error!("No argument or environment variable was given that points to the fassoc rules file.");
panic!()
}
}
};

let fassoc_rules = match read_fassoc_rules(proxy_rules_path) {
let fassoc_rules = match read_fassoc_rules(fassoc_rules_path) {
Ok(rules) => subst_arg_placeholders(rules, cli_args.to_owned()),
Err(error) => {
log::error!("Failure when reading fassoc rules ({})", error);
panic!();
}
};

let suitable_rule: &Rule = match fassoc_rules.find_suitable_rule(target_file_path) {
Ok(rule) => rule,
let suitable_command: &Command = match fassoc_rules.find_suitable_command(target_file_path) {
Ok(command) => command,
Err(error) => {
log::error!(
"Could not find a suitable rule for the file \"{}\", because: {}",
"Could not find a suitable command for the file \"{}\", because: {}",
target_file_name,
error
);
Expand All @@ -136,14 +136,14 @@ fn main() {

log::debug!(
"Creating process, path: \"{}\", args: \"{}\"",
suitable_rule.command.to_owned(),
suitable_rule
suitable_command.path.to_owned(),
suitable_command
.arguments
.to_owned()
.unwrap_or(String::from("NONE"))
);

match create_process(&suitable_rule) {
match invoke_command(&suitable_command) {
Ok(process_info) => {
log::debug!("Process created, information: {:?}", process_info)
}
Expand Down
157 changes: 97 additions & 60 deletions src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,35 @@ use serde::{Deserialize, Serialize};
// ----------------------------------------------------------------------------

#[derive(Debug)]
pub enum FindRuleError {
pub enum FindCommandError {
CannotConvertPath,
NoMappingFound,
NoRuleFound,
NoMatchFound,
}

impl std::fmt::Display for FindRuleError {
impl std::fmt::Display for FindCommandError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FindRuleError::CannotConvertPath => {
FindCommandError::CannotConvertPath => {
write!(f, "Could not convert the path of the file into a string.",)
}
FindRuleError::NoMappingFound => write!(f, "Could not find any applicable mappings."),
FindRuleError::NoRuleFound => write!(f, "Could not find any applicable rules."),
FindCommandError::NoMappingFound => write!(f, "No mapping could map the file to a matcher."),
FindCommandError::NoMatchFound => write!(f, "No matcher could match the file to a command."),
}
}
}

#[derive(Serialize, Deserialize, Debug)]
pub struct FassocRules {
pub mappings: HashMap<String, Vec<String>>,
pub rules: HashMap<String, Rule>,
pub matchers: HashMap<String, Matcher>,
pub commands: HashMap<String, Command>,
}

impl FassocRules {
pub fn find_suitable_rule(&self, file_path: &Path) -> Result<&Rule, FindRuleError> {
pub fn find_suitable_command(&self, file_path: &Path) -> Result<&Command, FindCommandError> {
let file_name_str: String = file_path.file_name().and_then(|n| n.to_str()).map_or_else(
|| Err(FindRuleError::CannotConvertPath),
|| Err(FindCommandError::CannotConvertPath),
|s| Ok(String::from(s)),
)?;

Expand All @@ -56,7 +57,7 @@ impl FassocRules {
Some(mapping) => mapping,
None => {
// Neither the extension mapping nor the fallback mapping found.
return Err(FindRuleError::NoMappingFound);
return Err(FindCommandError::NoMappingFound);
}
};

Expand All @@ -80,122 +81,158 @@ impl FassocRules {
}
};

for (index, rule_name) in final_mapping.iter().enumerate() {
log::debug!("Trying rule #{} - {}", index, rule_name);
let mapping_name = file_ext_str.to_owned().unwrap_or_else(|| String::from("*"));

let rule: &Rule = match self.rules.get(rule_name) {
Some(rule) => rule,
for (index, matcher_name) in final_mapping.iter().enumerate() {
log::debug!("Trying matcher #{} - {}", index, matcher_name);

let matcher: &Matcher = match self.matchers.get(matcher_name) {
Some(matcher) => matcher,
None => {
match self.commands.get(matcher_name) {
Some(command) => {
log::debug!(
"Mapping \"{}\" referred to \"{}\" which isn't a valid matcher, but it is a valid command, mapping directly to command instead.",
mapping_name,
matcher_name
);

return Ok(command);
},

None => {
log::warn!(
"Ignored a matcher with mame \"{}\" from mapping \"{}\" because it doesn't point to anything that exists.",
matcher_name,
mapping_name
);

continue;
}
}

}
};

let matcher_command: &Command = match self.commands.get(&matcher.command) {
Some(command) => command,
None => {
log::warn!(
"Ignored a rule name \"{}\" from mapping \"{}\", because it doesn't exist.",
rule_name,
file_ext_str.to_owned().unwrap_or(String::from("*"))
);
log::warn!("The command pointed to by matcher \"{}\" does not exist, ignoring this matcher.", matcher_name);
continue;
}
};

let mut valid = true;
let mut is_match: bool = true;

// If rule has file name RegEx, validate that the RegEx matches.
valid &= rule.regexf.as_ref().map_or(true, |_| {
rule.rmatch_file_name(file_name_str.to_owned())
// If matcher has file name RegEx, match the RegEx against the file.
is_match &= matcher.regexf.as_ref().map_or(true, |_| {
matcher.rmatch_file_name(file_name_str.to_owned())
.unwrap_or_else(|error| {
log::error!(
"Encountered RegEx error when evaluating mapped rules: {:?}",
"Encountered RegEx error when evaluating mapped matchers: {:?}",
error
);
false
})
});

// If already invalidated, continue.
if !valid {
// If match already failed, continue.
if !is_match {
continue;
}

// If rule has file content RegEx, validate that the RegEx matches.
valid &= rule.regexc.as_ref().map_or(true, |_| {
// If matcher has file content RegEx, match the RegEx against the file.
is_match &= matcher.regexc.as_ref().map_or(true, |_| {
ensure_contents_read(&mut file_content);

file_content.as_ref().map_or(false, |content| {
rule.rmatch_file_content(content).unwrap_or_else(|error| {
matcher.rmatch_file_content(content).unwrap_or_else(|error| {
log::error!(
"Encountered RegEx error when evaluating mapped rules: {:?}",
"Encountered RegEx error when evaluating mapped matchers: {:?}",
error,
);
false
})
})
});

// If the rule is still valid after validation, return it.
if valid {
// If the matcher is still valid after validation, return it.
if is_match {
log::debug!(
"Rule #{} - {} - is suitable for this file, using this rule.",
"Matcher #{} - {} - matched this file.",
index,
rule_name
matcher_name
);

return Ok(rule);

return Ok(matcher_command);
}
}

return Err(FindRuleError::NoRuleFound);
return Err(FindCommandError::NoMatchFound);
}
}

// ----------------------------------------------------------------------------
// Rule
// Matcher
// ----------------------------------------------------------------------------
#[derive(Debug)]
pub enum RuleRegexError {
pub enum MatcherError {
RegexCompileError(re::Error),
NoRegexError,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Rule {
pub struct Matcher {
pub command: String,
pub arguments: Option<String>,
pub cwd: Option<String>,
pub regexf: Option<String>,
pub regexc: Option<String>,
pub process_attributes: Option<SecurityAttributes>,
pub thread_attributes: Option<SecurityAttributes>,
pub inherit_handles: Option<bool>,
pub creation_flags: Option<Vec<sj::Value>>,
// pub environment: Option<Vec<sj::Value>>, -- This will be implemented later.
pub extras: Option<Extras>,
}

impl Rule {
fn rmatch_file(regstr: Option<String>, content: &String) -> Result<bool, RuleRegexError> {
regstr.map_or(Err(RuleRegexError::NoRegexError), |regstr| {
impl Matcher {
fn rmatch_file(regstr: Option<String>, content: &String) -> Result<bool, MatcherError> {
regstr.map_or(Err(MatcherError::NoRegexError), |regstr| {
re::Regex::new(regstr.as_str()).map_or_else(
|error| Err(RuleRegexError::RegexCompileError(error)),
|error| Err(MatcherError::RegexCompileError(error)),
|regex| Ok(regex.is_match(content.as_str())),
)
})
}

pub fn rmatch_file_name(&self, file_name: String) -> Result<bool, RuleRegexError> {
Rule::rmatch_file(self.regexf.to_owned(), &file_name)
pub fn rmatch_file_name(&self, file_name: String) -> Result<bool, MatcherError> {
Matcher::rmatch_file(self.regexf.to_owned(), &file_name)
}

pub fn rmatch_file_content(&self, file_content: &String) -> Result<bool, RuleRegexError> {
Rule::rmatch_file(self.regexc.to_owned(), file_content)
pub fn rmatch_file_content(&self, file_content: &String) -> Result<bool, MatcherError> {
Matcher::rmatch_file(self.regexc.to_owned(), file_content)
}
}

impl Clone for Rule {
// ----------------------------------------------------------------------------
// Command
// ----------------------------------------------------------------------------

#[derive(Serialize, Deserialize, Debug)]
pub struct Command {
pub path: String,
pub arguments: Option<String>,
pub cwd: Option<String>,
pub process_attributes: Option<SecurityAttributes>,
pub thread_attributes: Option<SecurityAttributes>,
pub inherit_handles: Option<bool>,
pub creation_flags: Option<Vec<sj::Value>>,
// pub environment: Option<Vec<sj::Value>>, -- This will be implemented later.
pub extras: Option<Extras>,
}

impl Command {}

impl Clone for Command {
fn clone(&self) -> Self {
Rule {
command: self.command.clone(),
Command {
path: self.path.clone(),
arguments: self.arguments.clone(),
cwd: self.cwd.clone(),
regexf: self.regexf.clone(),
regexc: self.regexc.clone(),
process_attributes: self.process_attributes.clone(),
thread_attributes: self.thread_attributes.clone(),
inherit_handles: self.inherit_handles.clone(),
Expand Down
Loading

0 comments on commit e2ed7f8

Please sign in to comment.