use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ImplCategory {
Inherent,
Derive,
Conversion,
Iterator,
Io,
Operator,
Access,
Formatting,
Other,
}
const DERIVE_TRAITS: &[&str] = &[
"Clone",
"Copy",
"Debug",
"Default",
"PartialEq",
"Eq",
"Hash",
"PartialOrd",
"Ord",
];
const CONVERSION_TRAITS: &[&str] = &[
"From",
"Into",
"TryFrom",
"TryInto",
"AsRef",
"AsMut",
"Borrow",
"BorrowMut",
"ToOwned",
];
const ITERATOR_TRAITS: &[&str] = &[
"Iterator",
"IntoIterator",
"FromIterator",
"Extend",
"DoubleEndedIterator",
"ExactSizeIterator",
"FusedIterator",
];
const IO_TRAITS: &[&str] = &["Read", "Write", "Seek", "BufRead", "BufWrite"];
const OPERATOR_TRAITS: &[&str] = &[
"Add",
"Sub",
"Mul",
"Div",
"Rem",
"Neg",
"Not",
"BitAnd",
"BitOr",
"BitXor",
"Shl",
"Shr",
"AddAssign",
"SubAssign",
"MulAssign",
"DivAssign",
"RemAssign",
"BitAndAssign",
"BitOrAssign",
"BitXorAssign",
"ShlAssign",
"ShrAssign",
];
const ACCESS_TRAITS: &[&str] = &["Deref", "DerefMut", "Index", "IndexMut"];
const FORMATTING_TRAITS: &[&str] = &[
"Display", "LowerHex", "UpperHex", "Octal", "Binary", "Pointer", "LowerExp", "UpperExp",
];
impl ImplCategory {
#[must_use]
pub fn from_trait_path(path: Option<&str>) -> Self {
let Some(path) = path else {
return Self::Inherent;
};
let trait_name = path.rsplit("::").next().unwrap_or(path);
if path.contains("std::ops::") || path.contains("core::ops::") {
return Self::Operator;
}
if DERIVE_TRAITS.contains(&trait_name) {
Self::Derive
} else if CONVERSION_TRAITS.contains(&trait_name) {
Self::Conversion
} else if ITERATOR_TRAITS.contains(&trait_name) {
Self::Iterator
} else if IO_TRAITS.contains(&trait_name) {
Self::Io
} else if OPERATOR_TRAITS.contains(&trait_name) {
Self::Operator
} else if ACCESS_TRAITS.contains(&trait_name) {
Self::Access
} else if FORMATTING_TRAITS.contains(&trait_name) {
Self::Formatting
} else {
Self::Other
}
}
#[must_use]
pub const fn display_name(&self) -> &'static str {
match self {
Self::Inherent => "Implementations",
Self::Derive => "Derived Traits",
Self::Conversion => "Conversion",
Self::Iterator => "Iterator",
Self::Io => "I/O",
Self::Operator => "Operators",
Self::Access => "Deref & Indexing",
Self::Formatting => "Formatting",
Self::Other => "Other Traits",
}
}
#[must_use]
const fn sort_order(self) -> u8 {
match self {
Self::Inherent => 0, Self::Derive => 1, Self::Conversion => 2, Self::Access => 3, Self::Iterator => 4, Self::Operator => 5, Self::Formatting => 6, Self::Io => 7, Self::Other => 8, }
}
}
impl PartialOrd for ImplCategory {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ImplCategory {
fn cmp(&self, other: &Self) -> Ordering {
self.sort_order().cmp(&other.sort_order())
}
}
#[cfg(test)]
mod tests {
use super::*;
mod from_trait_path {
use super::*;
#[test]
fn none_returns_inherent() {
assert_eq!(ImplCategory::from_trait_path(None), ImplCategory::Inherent);
}
#[test]
fn empty_string_returns_other() {
assert_eq!(ImplCategory::from_trait_path(Some("")), ImplCategory::Other);
}
#[test]
fn clone_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Clone")),
ImplCategory::Derive
);
}
#[test]
fn copy_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Copy")),
ImplCategory::Derive
);
}
#[test]
fn debug_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Debug")),
ImplCategory::Derive
);
}
#[test]
fn default_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Default")),
ImplCategory::Derive
);
}
#[test]
fn partial_eq_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("PartialEq")),
ImplCategory::Derive
);
}
#[test]
fn eq_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Eq")),
ImplCategory::Derive
);
}
#[test]
fn hash_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Hash")),
ImplCategory::Derive
);
}
#[test]
fn partial_ord_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("PartialOrd")),
ImplCategory::Derive
);
}
#[test]
fn ord_is_derive() {
assert_eq!(
ImplCategory::from_trait_path(Some("Ord")),
ImplCategory::Derive
);
}
#[test]
fn from_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("From")),
ImplCategory::Conversion
);
}
#[test]
fn into_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("Into")),
ImplCategory::Conversion
);
}
#[test]
fn try_from_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("TryFrom")),
ImplCategory::Conversion
);
}
#[test]
fn try_into_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("TryInto")),
ImplCategory::Conversion
);
}
#[test]
fn as_ref_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("AsRef")),
ImplCategory::Conversion
);
}
#[test]
fn as_mut_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("AsMut")),
ImplCategory::Conversion
);
}
#[test]
fn borrow_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("Borrow")),
ImplCategory::Conversion
);
}
#[test]
fn borrow_mut_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("BorrowMut")),
ImplCategory::Conversion
);
}
#[test]
fn to_owned_is_conversion() {
assert_eq!(
ImplCategory::from_trait_path(Some("ToOwned")),
ImplCategory::Conversion
);
}
#[test]
fn iterator_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Iterator")),
ImplCategory::Iterator
);
}
#[test]
fn into_iterator_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("IntoIterator")),
ImplCategory::Iterator
);
}
#[test]
fn from_iterator_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("FromIterator")),
ImplCategory::Iterator
);
}
#[test]
fn extend_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Extend")),
ImplCategory::Iterator
);
}
#[test]
fn double_ended_iterator_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("DoubleEndedIterator")),
ImplCategory::Iterator
);
}
#[test]
fn exact_size_iterator_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("ExactSizeIterator")),
ImplCategory::Iterator
);
}
#[test]
fn fused_iterator_is_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("FusedIterator")),
ImplCategory::Iterator
);
}
#[test]
fn read_is_io() {
assert_eq!(
ImplCategory::from_trait_path(Some("Read")),
ImplCategory::Io
);
}
#[test]
fn write_is_io() {
assert_eq!(
ImplCategory::from_trait_path(Some("Write")),
ImplCategory::Io
);
}
#[test]
fn seek_is_io() {
assert_eq!(
ImplCategory::from_trait_path(Some("Seek")),
ImplCategory::Io
);
}
#[test]
fn buf_read_is_io() {
assert_eq!(
ImplCategory::from_trait_path(Some("BufRead")),
ImplCategory::Io
);
}
#[test]
fn buf_write_is_io() {
assert_eq!(
ImplCategory::from_trait_path(Some("BufWrite")),
ImplCategory::Io
);
}
#[test]
fn add_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Add")),
ImplCategory::Operator
);
}
#[test]
fn sub_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Sub")),
ImplCategory::Operator
);
}
#[test]
fn mul_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Mul")),
ImplCategory::Operator
);
}
#[test]
fn div_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Div")),
ImplCategory::Operator
);
}
#[test]
fn neg_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("Neg")),
ImplCategory::Operator
);
}
#[test]
fn bit_and_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("BitAnd")),
ImplCategory::Operator
);
}
#[test]
fn add_assign_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("AddAssign")),
ImplCategory::Operator
);
}
#[test]
fn std_ops_path_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("std::ops::Add")),
ImplCategory::Operator
);
}
#[test]
fn core_ops_path_is_operator() {
assert_eq!(
ImplCategory::from_trait_path(Some("core::ops::Sub")),
ImplCategory::Operator
);
}
#[test]
fn deref_is_access() {
assert_eq!(
ImplCategory::from_trait_path(Some("Deref")),
ImplCategory::Access
);
}
#[test]
fn deref_mut_is_access() {
assert_eq!(
ImplCategory::from_trait_path(Some("DerefMut")),
ImplCategory::Access
);
}
#[test]
fn index_is_access() {
assert_eq!(
ImplCategory::from_trait_path(Some("Index")),
ImplCategory::Access
);
}
#[test]
fn index_mut_is_access() {
assert_eq!(
ImplCategory::from_trait_path(Some("IndexMut")),
ImplCategory::Access
);
}
#[test]
fn display_is_formatting() {
assert_eq!(
ImplCategory::from_trait_path(Some("Display")),
ImplCategory::Formatting
);
}
#[test]
fn lower_hex_is_formatting() {
assert_eq!(
ImplCategory::from_trait_path(Some("LowerHex")),
ImplCategory::Formatting
);
}
#[test]
fn upper_hex_is_formatting() {
assert_eq!(
ImplCategory::from_trait_path(Some("UpperHex")),
ImplCategory::Formatting
);
}
#[test]
fn octal_is_formatting() {
assert_eq!(
ImplCategory::from_trait_path(Some("Octal")),
ImplCategory::Formatting
);
}
#[test]
fn binary_is_formatting() {
assert_eq!(
ImplCategory::from_trait_path(Some("Binary")),
ImplCategory::Formatting
);
}
#[test]
fn pointer_is_formatting() {
assert_eq!(
ImplCategory::from_trait_path(Some("Pointer")),
ImplCategory::Formatting
);
}
#[test]
fn full_path_clone() {
assert_eq!(
ImplCategory::from_trait_path(Some("std::clone::Clone")),
ImplCategory::Derive
);
}
#[test]
fn full_path_from() {
assert_eq!(
ImplCategory::from_trait_path(Some("std::convert::From")),
ImplCategory::Conversion
);
}
#[test]
fn full_path_iterator() {
assert_eq!(
ImplCategory::from_trait_path(Some("std::iter::Iterator")),
ImplCategory::Iterator
);
}
#[test]
fn full_path_display() {
assert_eq!(
ImplCategory::from_trait_path(Some("std::fmt::Display")),
ImplCategory::Formatting
);
}
#[test]
fn full_path_read() {
assert_eq!(
ImplCategory::from_trait_path(Some("std::io::Read")),
ImplCategory::Io
);
}
#[test]
fn unknown_trait_is_other() {
assert_eq!(
ImplCategory::from_trait_path(Some("UnknownTrait")),
ImplCategory::Other
);
}
#[test]
fn serde_serialize_is_other() {
assert_eq!(
ImplCategory::from_trait_path(Some("serde::Serialize")),
ImplCategory::Other
);
}
#[test]
fn custom_trait_is_other() {
assert_eq!(
ImplCategory::from_trait_path(Some("my_crate::MyTrait")),
ImplCategory::Other
);
}
#[test]
fn send_is_other() {
assert_eq!(
ImplCategory::from_trait_path(Some("Send")),
ImplCategory::Other
);
}
#[test]
fn sync_is_other() {
assert_eq!(
ImplCategory::from_trait_path(Some("Sync")),
ImplCategory::Other
);
}
#[test]
fn drop_is_other() {
assert_eq!(
ImplCategory::from_trait_path(Some("Drop")),
ImplCategory::Other
);
}
}
mod display_name {
use super::*;
#[test]
fn inherent_display_name() {
assert_eq!(ImplCategory::Inherent.display_name(), "Implementations");
}
#[test]
fn derive_display_name() {
assert_eq!(ImplCategory::Derive.display_name(), "Derived Traits");
}
#[test]
fn conversion_display_name() {
assert_eq!(ImplCategory::Conversion.display_name(), "Conversion");
}
#[test]
fn iterator_display_name() {
assert_eq!(ImplCategory::Iterator.display_name(), "Iterator");
}
#[test]
fn io_display_name() {
assert_eq!(ImplCategory::Io.display_name(), "I/O");
}
#[test]
fn operator_display_name() {
assert_eq!(ImplCategory::Operator.display_name(), "Operators");
}
#[test]
fn access_display_name() {
assert_eq!(ImplCategory::Access.display_name(), "Deref & Indexing");
}
#[test]
fn formatting_display_name() {
assert_eq!(ImplCategory::Formatting.display_name(), "Formatting");
}
#[test]
fn other_display_name() {
assert_eq!(ImplCategory::Other.display_name(), "Other Traits");
}
}
mod ordering {
use super::*;
#[test]
fn inherent_is_first() {
assert!(ImplCategory::Inherent < ImplCategory::Derive);
assert!(ImplCategory::Inherent < ImplCategory::Other);
}
#[test]
fn other_is_last() {
assert!(ImplCategory::Other > ImplCategory::Inherent);
assert!(ImplCategory::Other > ImplCategory::Derive);
assert!(ImplCategory::Other > ImplCategory::Io);
}
#[test]
fn derive_before_conversion() {
assert!(ImplCategory::Derive < ImplCategory::Conversion);
}
#[test]
fn conversion_before_access() {
assert!(ImplCategory::Conversion < ImplCategory::Access);
}
#[test]
fn access_before_iterator() {
assert!(ImplCategory::Access < ImplCategory::Iterator);
}
#[test]
fn iterator_before_operator() {
assert!(ImplCategory::Iterator < ImplCategory::Operator);
}
#[test]
fn operator_before_formatting() {
assert!(ImplCategory::Operator < ImplCategory::Formatting);
}
#[test]
fn formatting_before_io() {
assert!(ImplCategory::Formatting < ImplCategory::Io);
}
#[test]
fn io_before_other() {
assert!(ImplCategory::Io < ImplCategory::Other);
}
#[test]
fn full_ordering() {
let mut categories = vec![
ImplCategory::Other,
ImplCategory::Formatting,
ImplCategory::Inherent,
ImplCategory::Operator,
ImplCategory::Derive,
ImplCategory::Io,
ImplCategory::Iterator,
ImplCategory::Access,
ImplCategory::Conversion,
];
categories.sort();
assert_eq!(
categories,
vec![
ImplCategory::Inherent,
ImplCategory::Derive,
ImplCategory::Conversion,
ImplCategory::Access,
ImplCategory::Iterator,
ImplCategory::Operator,
ImplCategory::Formatting,
ImplCategory::Io,
ImplCategory::Other,
]
);
}
#[test]
fn same_category_is_equal() {
assert_eq!(
ImplCategory::Derive.cmp(&ImplCategory::Derive),
Ordering::Equal
);
}
}
mod traits {
use std::collections::HashSet;
use super::*;
#[test]
fn category_is_copy() {
let cat = ImplCategory::Derive;
let cat2 = cat; assert_eq!(cat, cat2);
}
#[test]
fn category_is_clone() {
let cat = ImplCategory::Iterator;
let cat2 = cat;
assert_eq!(cat, cat2);
}
#[test]
fn category_is_hashable() {
let mut set = HashSet::new();
set.insert(ImplCategory::Derive);
set.insert(ImplCategory::Conversion);
set.insert(ImplCategory::Derive);
assert_eq!(set.len(), 2);
assert!(set.contains(&ImplCategory::Derive));
assert!(set.contains(&ImplCategory::Conversion));
}
#[test]
fn category_debug_format() {
let debug_str = format!("{:?}", ImplCategory::Formatting);
assert_eq!(debug_str, "Formatting");
}
}
}