Skip to content

Commit

Permalink
feat: more similar name suggestion (#59)
Browse files Browse the repository at this point in the history
* WIP

* done
  • Loading branch information
shixinhuang99 authored Mar 4, 2024
1 parent 7936e08 commit 51189fa
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Usage: sca [OPTIONS] [COMMAND]
Commands:
list List all templates
remove Remove specified templates
mv Rename a template
rename Rename a template
add Add template from GitHub repository
create Copy the template folder to the specified directory
token Configure or display your GitHub personal access token
Expand Down
4 changes: 2 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub enum Command {
Remove(RemoveArgs),

/// Rename a template
Mv(MvArgs),
Rename(RenameArgs),

/// Add template from GitHub repository
Add(AddArgs),
Expand All @@ -58,7 +58,7 @@ pub struct RemoveArgs {
}

#[derive(Args, Debug)]
pub struct MvArgs {
pub struct RenameArgs {
pub name: String,
pub new_name: String,
}
Expand Down
10 changes: 2 additions & 8 deletions src/colorize.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
macro_rules! trait_colorize {
($($method:ident),+) => {
macro_rules! impl_colorize_for_str {
($(($method:ident, $color:ident)),+) => {
pub trait Colorize {
$(
fn $method(&self) -> String;
)*
}
};
}

macro_rules! impl_colorize_for_str {
($(($method:ident, $color:ident)),+) => {
impl Colorize for str {
$(
#[cfg(not(test))]
Expand All @@ -28,8 +24,6 @@ macro_rules! impl_colorize_for_str {
};
}

trait_colorize!(blue, red, green);

impl_colorize_for_str!((blue, UserBlue), (red, UserRed), (green, UserGreen));

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn run() -> Result<()> {
match command {
Command::List(args) => scafalra.list(args),
Command::Remove(args) => scafalra.remove(args)?,
Command::Mv(args) => scafalra.mv(args)?,
Command::Rename(args) => scafalra.rename(args)?,
Command::Add(args) => scafalra.add(args)?,
Command::Create(args) => scafalra.create(args)?,
Command::Token(args) => scafalra.token(args)?,
Expand Down
1 change: 0 additions & 1 deletion src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ impl Repository {

#[cfg(test)]
mod tests {

use anyhow::Result;
use test_case::test_case;

Expand Down
17 changes: 8 additions & 9 deletions src/scafalra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use remove_dir_all::remove_dir_all;

use crate::{
api::GitHubApi,
cli::{AddArgs, CreateArgs, ListArgs, MvArgs, RemoveArgs, TokenArgs},
cli::{AddArgs, CreateArgs, ListArgs, RemoveArgs, RenameArgs, TokenArgs},
config::Config,
debug,
path_ext::*,
Expand Down Expand Up @@ -224,11 +224,8 @@ impl Scafalra {
debug!("args: {:#?}", args);

let Some(template) = self.store.get(&args.name) else {
let mut msg = format!("No such template `{}`", args.name);
if let Some(name) = self.store.get_similar_name(&args.name) {
msg.push_str(&format!("\nA similar template is `{}`", name));
}
anyhow::bail!(msg);
let suggestion = self.store.similar_name_suggestion(&args.name);
anyhow::bail!("{}", suggestion);
};

let cwd = env::current_dir()?;
Expand Down Expand Up @@ -284,12 +281,14 @@ impl Scafalra {
}
}

pub fn mv(&mut self, args: MvArgs) -> Result<()> {
pub fn rename(&mut self, args: RenameArgs) -> Result<()> {
debug!("args: {:#?}", args);

self.store.rename(&args.name, &args.new_name);
let is_renamed = self.store.rename(&args.name, &args.new_name);

self.store.save()?;
if is_renamed {
self.store.save()?;
}

Ok(())
}
Expand Down
56 changes: 46 additions & 10 deletions src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,28 @@ impl Store {
Ok(())
}

pub fn rename(&mut self, name: &str, new_name: &str) {
pub fn rename(&mut self, name: &str, new_name: &str) -> bool {
if self.templates.contains_key(new_name) {
println!("`{}` already exists", new_name);
return;
return false;
}

match self.templates.remove(name) {
let ret = match self.templates.remove(name) {
Some(template) => {
self.templates.insert(new_name.to_string(), template);
self.changes.push_remove(name).push_add(new_name);

true
}
None => {
println!("No such template `{}`", name);
let suggestion = self.similar_name_suggestion(name);
println!("{}", suggestion);

false
}
};

ret
}

pub fn print_grid(&self) -> Option<String> {
Expand Down Expand Up @@ -254,10 +261,14 @@ impl Store {
self.templates.get(name)
}

pub fn get_similar_name(&self, target: &str) -> Option<&str> {
pub fn similar_name_suggestion<'a: 'b, 'b>(
&'a self,
target: &'a str,
) -> Suggestion<'b> {
use strsim::normalized_levenshtein;

self.templates
let similar = self
.templates
.keys()
.filter_map(|name| {
let score = normalized_levenshtein(target, name).abs();
Expand All @@ -266,8 +277,30 @@ impl Store {
}
None
})
.min_by(|x, y| x.1.total_cmp(&y.1))
.map(|v| v.0.as_str())
.max_by(|x, y| x.1.total_cmp(&y.1))
.map(|v| v.0.as_str());

Suggestion {
target,
similar,
}
}
}

pub struct Suggestion<'a> {
pub target: &'a str,
pub similar: Option<&'a str>,
}

impl std::fmt::Display for Suggestion<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut msg = format!("No such template `{}`", self.target);

if let Some(similar) = self.similar {
msg.push_str(&format!("\nA similar template is `{}`", similar));
}

write!(f, "{}", msg)
}
}

Expand Down Expand Up @@ -520,9 +553,12 @@ mod tests {
}

#[test]
fn test_similar_name() {
fn test_store_similar_name() {
let store_mock = StoreMock::new().with_content();

assert_eq!(store_mock.store.get_similar_name("fop"), Some("foo"));
assert_eq!(
store_mock.store.similar_name_suggestion("fop").similar,
Some("foo")
);
}
}

0 comments on commit 51189fa

Please sign in to comment.