use once_cell::sync::Lazy;
use std::{
collections::{HashMap, HashSet},
error::Error,
fmt::{self, Display},
};
use crate::{NewName, NewNameRule, ReturnsBool};
pub static RESERVED: Lazy<HashSet<&'static str>> = Lazy::new(|| {
let mut reserved = HashSet::new();
reserved.insert("");
reserved.insert("as");
reserved.insert("const");
reserved.insert("else");
reserved.insert("false");
reserved.insert("for");
reserved.insert("if");
reserved.insert("in");
reserved.insert("mut");
reserved.insert("optional");
reserved.insert("owned");
reserved.insert("ref");
reserved.insert("some");
reserved.insert("true");
reserved.insert("unchecked_mut");
reserved.insert("where");
reserved.insert("while");
reserved
});
pub static EXACT_SUFFIX_SUBSTITUTES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
let mut exact_subs = HashMap::new();
exact_subs.insert("async", "async_");
exact_subs.insert("await", "await_");
exact_subs.insert("break", "break_");
exact_subs.insert("crate", "crate_");
exact_subs.insert("continue", "continue_");
exact_subs.insert("dyn", "dyn_");
exact_subs.insert("enum", "enum_");
exact_subs.insert("extern", "extern_");
exact_subs.insert("fn", "fn_");
exact_subs.insert("impl", "impl_");
exact_subs.insert("loop", "loop_");
exact_subs.insert("match", "match_");
exact_subs.insert("mod", "mod_");
exact_subs.insert("move", "move_");
exact_subs.insert("pub", "pub_");
exact_subs.insert("return", "return_");
exact_subs.insert("self", "self_");
exact_subs.insert("static", "static_");
exact_subs.insert("struct", "struct_");
exact_subs.insert("super", "super_");
exact_subs.insert("trait", "trait_");
exact_subs.insert("type", "type_");
exact_subs.insert("union", "union_");
exact_subs.insert("unsafe", "unsafe_");
exact_subs.insert("use", "use_");
exact_subs
});
pub static BOOL_FIRST_TOKEN_SUBSTITUTES: Lazy<HashMap<&'static str, &'static str>> =
Lazy::new(|| {
let mut first_token_subs = HashMap::new();
first_token_subs.insert("activate", "activates");
first_token_subs.insert("accept", "accepts");
first_token_subs.insert("allow", "allows");
first_token_subs.insert("always", "must_always");
first_token_subs.insert("close", "closes");
first_token_subs.insert("create", "creates");
first_token_subs.insert("destroy", "must_destroy");
first_token_subs.insert("do", "does");
first_token_subs.insert("draw", "draws");
first_token_subs.insert("embed", "embeds");
first_token_subs.insert("emit", "emits");
first_token_subs.insert("enable", "enables");
first_token_subs.insert("exit", "exits");
first_token_subs.insert("expand", "expands");
first_token_subs.insert("fill", "fills");
first_token_subs.insert("fit", "fits");
first_token_subs.insert("focus", "gets_focus");
first_token_subs.insert("follow", "follows");
first_token_subs.insert("hide", "hides");
first_token_subs.insert("ignore", "ignores");
first_token_subs.insert("invert", "inverts");
first_token_subs.insert("mute", "is_muted");
first_token_subs.insert("propagate", "propagates");
first_token_subs.insert("populate", "populates");
first_token_subs.insert("receive", "receives");
first_token_subs.insert("reset", "resets");
first_token_subs.insert("require", "requires");
first_token_subs.insert("reserve", "must_reserve");
first_token_subs.insert("resize", "resizes");
first_token_subs.insert("restrict", "restricts");
first_token_subs.insert("reveal", "reveals");
first_token_subs.insert("select", "selects");
first_token_subs.insert("show", "shows");
first_token_subs.insert("skip", "skips");
first_token_subs.insert("snap", "snaps");
first_token_subs.insert("support", "supports");
first_token_subs.insert("take", "takes");
first_token_subs.insert("track", "tracks");
first_token_subs.insert("truncate", "must_truncate");
first_token_subs.insert("use", "uses");
first_token_subs.insert("wrap", "wraps");
first_token_subs
});
pub static BOOL_FIRST_TOKEN_NO_PREFIX: Lazy<HashSet<&'static str>> = Lazy::new(|| {
let mut first_tokens = HashSet::new();
first_tokens.insert("can");
first_tokens.insert("has");
first_tokens.insert("must");
first_tokens.insert("should");
first_tokens.insert("state");
for bool_substitute in BOOL_FIRST_TOKEN_SUBSTITUTES.values() {
first_tokens.insert(bool_substitute);
}
first_tokens
});
pub static BOOL_EXACT_SUBSTITUTES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
let mut exact_subs = HashMap::new();
exact_subs.insert("result", "result");
exact_subs.insert("overwrite", "overwrites");
exact_subs.insert("visibility", "is_visible");
exact_subs
});
pub const BOOL_ABLE_PREFIX: &str = "able";
pub static PREFIX_TO_POSTFIX: Lazy<HashSet<&'static str>> = Lazy::new(|| {
let mut prefix_to_postfix = HashSet::new();
prefix_to_postfix.insert("mut");
prefix_to_postfix
});
pub fn try_rename_would_be_getter(
name: &str,
returns_bool: impl Into<ReturnsBool>,
) -> Result<NewName, RenameError> {
let suffix = match name.strip_prefix("get_") {
Some(suffix) => suffix,
None => return Err(RenameError::NotGetFn),
};
try_rename_getter_suffix(suffix, returns_bool)
}
pub fn try_rename_getter_suffix(
suffix: &str,
returns_bool: impl Into<ReturnsBool>,
) -> Result<NewName, RenameError> {
use ReturnsBool::*;
let returns_bool = match returns_bool.into() {
False => ReturnsBool::False,
True => return Ok(rename_bool_getter(suffix)),
Maybe => {
if let Some(rename) = guesstimate_boolness_then_rename(suffix) {
return Ok(rename);
}
ReturnsBool::Maybe
}
};
if let Some(substitute) = EXACT_SUFFIX_SUBSTITUTES.get(suffix) {
return Ok(NewName {
new_name: substitute.to_string(),
returns_bool,
rule: NewNameRule::Substituted,
});
}
let splits: Vec<&str> = suffix.splitn(2, '_').collect();
if splits.len() > 1 && PREFIX_TO_POSTFIX.contains(splits[0]) {
Ok(NewName {
new_name: format!("{}_{}", splits[1], splits[0]),
returns_bool,
rule: NewNameRule::Fixed,
})
} else if RESERVED.contains(suffix) {
Err(RenameError::Reserved)
} else {
Ok(NewName {
new_name: suffix.to_string(),
returns_bool,
rule: NewNameRule::Regular,
})
}
}
#[inline]
pub fn rename_bool_getter(suffix: &str) -> NewName {
if let Some(substitute) = BOOL_EXACT_SUBSTITUTES.get(suffix) {
return NewName {
new_name: substitute.to_string(),
returns_bool: true.into(),
rule: NewNameRule::Substituted,
};
}
if let Some(new_name) = try_rename_bool_getter(suffix) {
new_name
} else {
NewName {
new_name: format!("is_{}", suffix),
returns_bool: true.into(),
rule: NewNameRule::Regular,
}
}
}
#[inline]
fn try_rename_bool_getter(suffix: &str) -> Option<NewName> {
let mut working_suffix = suffix;
let mut has_is_prefix = false;
if let Some(suffix_without_is) = suffix.strip_prefix("is_") {
working_suffix = suffix_without_is;
has_is_prefix = true;
}
let splits: Vec<&str> = working_suffix.splitn(2, '_').collect();
BOOL_FIRST_TOKEN_SUBSTITUTES
.get(splits[0])
.map(|substitute| {
if splits.len() == 1 {
NewName {
new_name: substitute.to_string(),
returns_bool: true.into(),
rule: NewNameRule::Substituted,
}
} else {
NewName {
new_name: format!("{}_{}", substitute, splits[1]),
returns_bool: true.into(),
rule: NewNameRule::Substituted,
}
}
})
.or_else(|| {
BOOL_FIRST_TOKEN_NO_PREFIX.get(splits[0]).map(|_| {
if splits.len() == 1 {
NewName {
new_name: splits[0].to_string(),
returns_bool: true.into(),
rule: NewNameRule::NoPrefix,
}
} else {
NewName {
new_name: format!("{}_{}", splits[0], splits[1]),
returns_bool: true.into(),
rule: NewNameRule::NoPrefix,
}
}
})
})
.or_else(|| {
if has_is_prefix {
Some(NewName {
new_name: suffix.to_string(),
returns_bool: true.into(),
rule: NewNameRule::Regular,
})
} else {
None
}
})
}
#[inline]
pub fn guesstimate_boolness_then_rename(suffix: &str) -> Option<NewName> {
if let Some(new_name) = try_rename_bool_getter(suffix) {
return Some(new_name);
}
let splits: Vec<&str> = suffix.splitn(2, '_').collect();
if splits[0].ends_with(BOOL_ABLE_PREFIX) {
Some(NewName {
new_name: format!("is_{}", suffix),
returns_bool: true.into(),
rule: NewNameRule::Regular,
})
} else {
None
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[non_exhaustive]
pub enum RenameError {
NotGetFn,
Reserved,
}
impl RenameError {
pub fn is_not_get_fn(&self) -> bool {
matches!(self, RenameError::NotGetFn)
}
pub fn is_reserved(&self) -> bool {
matches!(self, RenameError::Reserved)
}
}
impl Display for RenameError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use RenameError::*;
match self {
NotGetFn => write!(f, "not a get function"),
Reserved => write!(f, "name is reserved"),
}
}
}
impl Error for RenameError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bool_getter_rename_attempt() {
let new_name = try_rename_bool_getter(&"mute").unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_muted");
let new_name = try_rename_bool_getter(&"emit_eos").unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_bool_getter(&"has_entry").unwrap();
assert!(new_name.is_no_prefix());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "has_entry");
let new_name = try_rename_bool_getter(&"is_emit_eos").unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_bool_getter(&"is_activated").unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activated");
assert!(try_rename_bool_getter(&"name").is_none());
}
#[test]
fn bool_getter_suffix() {
let new_name = rename_bool_getter(&"result");
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "result");
let new_name = rename_bool_getter(&"activable");
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activable");
let new_name = rename_bool_getter(&"mute");
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_muted");
let new_name = rename_bool_getter(&"emit_eos");
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = rename_bool_getter(&"can_acquire");
assert!(new_name.is_no_prefix());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "can_acquire");
}
#[test]
fn boolness_guestimation() {
assert!(guesstimate_boolness_then_rename(&"result").is_none());
assert!(guesstimate_boolness_then_rename(&"name").is_none());
let new_name = guesstimate_boolness_then_rename(&"mute").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_muted");
let new_name = guesstimate_boolness_then_rename(&"does_ts").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "does_ts");
let new_name = guesstimate_boolness_then_rename(&"emit_eos").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = guesstimate_boolness_then_rename(&"emits_eos").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = guesstimate_boolness_then_rename(&"is_emits_eos").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = guesstimate_boolness_then_rename(&"is_activated").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activated");
let new_name = guesstimate_boolness_then_rename(&"activable").unwrap();
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activable");
}
#[test]
fn rename_getter_non_bool() {
let new_name = try_rename_would_be_getter(&"get_structure", false).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_false());
assert_eq!(new_name, "structure");
let new_name = try_rename_would_be_getter(&"get_type", false).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_false());
assert_eq!(new_name, "type_");
let new_name = try_rename_would_be_getter(&"get_activable", false).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_false());
assert_eq!(new_name, "activable");
let new_name = try_rename_would_be_getter(&"get_mut_structure", false).unwrap();
assert!(new_name.is_fixed());
assert!(new_name.returns_bool().is_false());
assert_eq!(new_name, "structure_mut");
assert!(try_rename_would_be_getter(&"get_mut", false)
.unwrap_err()
.is_reserved());
assert!(try_rename_would_be_getter(&"not_a_getter", false)
.unwrap_err()
.is_not_get_fn());
}
#[test]
fn rename_getter_bool() {
let new_name = try_rename_would_be_getter(&"get_structure", true).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_structure");
let new_name = try_rename_would_be_getter(&"get_type", true).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_type");
let new_name = try_rename_would_be_getter(&"get_mute", true).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_muted");
let new_name = try_rename_would_be_getter(&"get_emit_eos", true).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_would_be_getter(&"get_emits_eos", true).unwrap();
assert!(new_name.is_no_prefix());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_would_be_getter(&"get_is_emit_eos", true).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_would_be_getter(&"get_is_activated", true).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activated");
let new_name = try_rename_would_be_getter(&"get_activable", true).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activable");
let new_name = try_rename_would_be_getter(&"get_mut", true).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_mut");
let new_name = try_rename_would_be_getter(&"get_overwrite", true).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "overwrites");
let new_name = try_rename_would_be_getter(&"get_overwrite_mode", true).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_overwrite_mode");
assert!(try_rename_would_be_getter(&"not_a_getter", true)
.unwrap_err()
.is_not_get_fn());
}
#[test]
fn rename_getter_maybe_bool() {
let new_name = try_rename_would_be_getter(&"get_structure", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_maybe());
assert_eq!(new_name, "structure");
let new_name = try_rename_would_be_getter(&"get_type", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_maybe());
assert_eq!(new_name, "type_");
let new_name = try_rename_would_be_getter(&"get_mute", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_muted");
let new_name = try_rename_would_be_getter(&"get_emit_eos", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_would_be_getter(&"get_emits_eos", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_no_prefix());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_would_be_getter(&"get_is_emit_eos", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_substituted());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "emits_eos");
let new_name = try_rename_would_be_getter(&"get_is_activated", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activated");
let new_name = try_rename_would_be_getter(&"get_activable", ReturnsBool::Maybe).unwrap();
assert!(new_name.is_regular());
assert!(new_name.returns_bool().is_true());
assert_eq!(new_name, "is_activable");
assert!(try_rename_would_be_getter(&"get_mut", ReturnsBool::Maybe)
.unwrap_err()
.is_reserved());
assert!(
try_rename_would_be_getter(&"not_a_getter", ReturnsBool::Maybe)
.unwrap_err()
.is_not_get_fn()
);
}
}