Skip to content

Commit

Permalink
FEAT: Add rule: hostscli (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
luizvbo authored Apr 17, 2024
1 parent 5735657 commit 06e52cb
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 3 deletions.
92 changes: 92 additions & 0 deletions src/rules/hostscli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use super::{
get_new_command_without_sudo, match_rule_without_sudo, utils::match_rule_with_is_app, Rule,
};
use crate::{cli::command::CrabCommand, shell::Shell, utils::replace_command};
use regex::Regex;

const NO_COMMAND: &str = "Error: No such command";
const NO_WEBSITE: &str = "hostscli.errors.WebsiteImportError";

fn auxiliary_match_rule(command: &CrabCommand) -> bool {
if let Some(output) = &command.output {
output.contains(NO_COMMAND) || output.contains(NO_WEBSITE)
} else {
false
}
}

pub fn match_rule(command: &mut CrabCommand, system_shell: Option<&dyn Shell>) -> bool {
match_rule_without_sudo(
|command| match_rule_with_is_app(auxiliary_match_rule, command, vec!["hostscli"], None),
command,
)
}

pub fn auxiliary_get_new_command(command: &CrabCommand) -> Vec<String> {
if let Some(output) = &command.output {
if output.contains(NO_WEBSITE) {
vec!["hostscli websites".to_owned()]
} else {
let misspelled_command = Regex::new(r#"Error: No such command "(.*)""#)
.unwrap()
.captures(output)
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_owned()));
match misspelled_command {
None => vec![],
Some(misspelled_command) => replace_command(
command,
&misspelled_command,
vec!["block", "unblock", "websites", "block_all", "unblock_all"],
),
}
}
} else {
vec![]
}
}

pub fn get_new_command(command: &mut CrabCommand, system_shell: Option<&dyn Shell>) -> Vec<String> {
get_new_command_without_sudo(auxiliary_get_new_command, command)
}

pub fn get_rule() -> Rule {
Rule::new(
"hostscli".to_owned(),
None,
None,
None,
match_rule,
get_new_command,
None,
)
}

#[cfg(test)]
mod tests {
use super::{get_new_command, match_rule};
use crate::cli::command::CrabCommand;
use crate::shell::Bash;
use rstest::rstest;

const ERROR_NO_WEBSITE: &str = "\nhostscli.errors.WebsiteImportError:\n\nNo Domain list found for website: a_website_that_does_not_exist\n\nPlease raise a Issue here: https://github.com/dhilipsiva/hostscli/issues/new\nif you think we should add domains for this website.\n\ntype `hostscli websites` to see a list of websites that you can block/unblock\n";

#[rstest]
#[case("hostscli block a_website_that_does_not_exist", ERROR_NO_WEBSITE, true)]
fn test_match(#[case] command: &str, #[case] stdout: &str, #[case] is_match: bool) {
let mut command = CrabCommand::new(command.to_owned(), Some(stdout.to_owned()), None);
assert_eq!(match_rule(&mut command, None), is_match);
}

#[rstest]
#[case("hostscli block a_website_that_does_not_exist", ERROR_NO_WEBSITE, vec!["hostscli websites"])]
#[case("hostscli websitess", r#"Error: No such command "websitess""#, vec!["hostscli websites"])]
fn test_get_new_command(
#[case] command: &str,
#[case] stdout: &str,
#[case] expected: Vec<&str>,
) {
let system_shell = Bash {};
let mut command = CrabCommand::new(command.to_owned(), Some(stdout.to_owned()), None);
assert_eq!(get_new_command(&mut command, None), expected);
}
}
2 changes: 2 additions & 0 deletions src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ mod has_exists_script;
mod heroku_multiple_apps;
mod heroku_not_command;
mod history;
mod hostscli;
mod java;
mod javac;
mod lein_not_task;
Expand Down Expand Up @@ -167,6 +168,7 @@ pub fn get_rules() -> Vec<Rule> {
heroku_multiple_apps::get_rule(),
heroku_not_command::get_rule(),
history::get_rule(),
hostscli::get_rule(),
java::get_rule(),
javac::get_rule(),
lein_not_task::get_rule(),
Expand Down
4 changes: 2 additions & 2 deletions src/rules/npm_missing_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ npm ERR! /home/nvbn/exp/code_view/client_web/npm-debug.log
}

#[rstest]
#[case("npm ru wach-tests", &output("wach-tests"), vec!["npm ru watch-test", "npm ru develop"])]
#[case("npm -i run-script dvelop", &output("dvelop"), vec!["npm -i run-script develop", "npm -i run-script build", "npm -i run-script watch-test"])]
#[case("npm ru wach-tests", &output("wach-tests"), vec!["npm ru watch-test"])]
#[case("npm -i run-script dvelop", &output("dvelop"), vec!["npm -i run-script develop", "npm -i run-script build"])]
#[case("npm -i run-script buld -X POST", &output("buld"), vec!["npm -i run-script build -X POST", "npm -i run-script develop -X POST"])]
fn test_get_new_command(
#[case] command: &str,
Expand Down
28 changes: 27 additions & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,34 @@ pub fn replace_argument(script: &str, from_: &str, to: &str) -> String {
}
}

/// Replaces a broken command with close matches from a list of candidate commands.
///
/// Given a `CrabCommand` and a `broken` command string, this function attempts to find close matches
/// within a provided list of `matched` candidate strings. It then replaces the `broken` command in the
/// script of the `CrabCommand` with each of the close matches, trimming any leading or trailing whitespace.
///
/// # Arguments
///
/// * `command` - A reference to a `CrabCommand` that holds the script where the replacement should occur.
/// * `broken` - The string representing the broken command to be replaced.
/// * `matched` - A vector of string slices representing the candidate commands for replacement.
///
/// # Returns
///
/// This function returns a vector of strings, each containing the script of the `CrabCommand` with the
/// `broken` command replaced by one of the close matches.
///
/// # Examples
///
/// ```
/// let command = CrabCommand { script: "echo broken_command" };
/// let broken = "broken_command";
/// let matched = vec!["fixed_command1", "fixed_command2"];
/// let fixed_scripts = replace_command(&command, broken, matched);
/// assert_eq!(fixed_scripts, vec!["echo fixed_command1", "echo fixed_command2"]);
/// ```
pub fn replace_command(command: &CrabCommand, broken: &str, matched: Vec<&str>) -> Vec<String> {
let candidate_commands = get_close_matches(broken, &matched, None, Some(0.1));
let candidate_commands = get_close_matches(broken, &matched, None, Some(0.2));
let mut new_commands = Vec::<String>::new();
for cmd in candidate_commands {
new_commands.push(replace_argument(&command.script, broken, cmd.trim()));
Expand Down

0 comments on commit 06e52cb

Please sign in to comment.