#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![cfg_attr(test, allow(clippy::non_ascii_literal))]
#![cfg_attr(test, allow(clippy::shadow_unrelated))]
#![warn(clippy::cargo)]
#![allow(unknown_lints)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_qualifications)]
#![warn(variant_size_differences)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_alias))]
#![cfg_attr(
not(feature = "std"),
doc = "[`std`]: https://doc.rust-lang.org/std/index.html"
)]
#![cfg_attr(
not(feature = "std"),
doc = "[`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html"
)]
#![cfg_attr(
not(feature = "alloc"),
doc = "[`alloc`]: https://doc.rust-lang.org/alloc/index.html"
)]
#![cfg_attr(feature = "alloc", doc = "[`String`]: alloc::string::String")]
#![cfg_attr(
not(feature = "alloc"),
doc = "[`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html"
)]
#![cfg_attr(feature = "alloc", doc = "[`Vec`]: alloc::vec::Vec")]
#![cfg_attr(
not(feature = "alloc"),
doc = "[`Vec`]: https://doc.rust-lang.org/alloc/vec/struct.Vec.html"
)]
#![no_std]
#![doc(html_root_url = "https://docs.rs/roe/0.0.4")]
#[cfg(any(feature = "alloc", test))]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::str::FromStr;
mod ascii;
mod lowercase;
mod uppercase;
pub use ascii::{make_ascii_lowercase, make_ascii_titlecase, make_ascii_uppercase};
#[cfg(feature = "alloc")]
pub use ascii::{to_ascii_lowercase, to_ascii_titlecase, to_ascii_uppercase};
pub use lowercase::Lowercase;
pub use uppercase::Uppercase;
#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct InvalidCaseMappingMode {
_private: (),
}
impl InvalidCaseMappingMode {
#[must_use]
pub const fn new() -> Self {
Self { _private: () }
}
#[must_use]
#[allow(clippy::unused_self)]
pub const fn message(self) -> &'static str {
"invalid option"
}
}
impl fmt::Display for InvalidCaseMappingMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const MESSAGE: &str = InvalidCaseMappingMode::new().message();
f.write_str(MESSAGE)
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidCaseMappingMode {}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum LowercaseMode {
Full,
Ascii,
Turkic,
Lithuanian,
Fold,
}
impl Default for LowercaseMode {
fn default() -> Self {
Self::Full
}
}
impl TryFrom<&str> for LowercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.as_bytes().try_into()
}
}
impl TryFrom<Option<&str>> for LowercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: Option<&str>) -> Result<Self, Self::Error> {
value.map(str::as_bytes).try_into()
}
}
impl TryFrom<&[u8]> for LowercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value {
b"ascii" => Ok(Self::Ascii),
b"turkic" => Ok(Self::Turkic),
b"lithuanian" => Ok(Self::Lithuanian),
b"fold" => Ok(Self::Fold),
_ => Err(InvalidCaseMappingMode::new()),
}
}
}
impl TryFrom<Option<&[u8]>> for LowercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: Option<&[u8]>) -> Result<Self, Self::Error> {
match value {
None => Ok(Self::Full),
Some(b"ascii") => Ok(Self::Ascii),
Some(b"turkic") => Ok(Self::Turkic),
Some(b"lithuanian") => Ok(Self::Lithuanian),
Some(b"fold") => Ok(Self::Fold),
Some(_) => Err(InvalidCaseMappingMode::new()),
}
}
}
impl FromStr for LowercaseMode {
type Err = InvalidCaseMappingMode;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
pub fn lowercase(slice: &[u8], options: LowercaseMode) -> Lowercase<'_> {
match options {
LowercaseMode::Full | LowercaseMode::Lithuanian => Lowercase::with_slice(slice),
LowercaseMode::Ascii => Lowercase::with_ascii_slice(slice),
LowercaseMode::Turkic => panic!("lowercase Turkic mode is not yet implemented"),
LowercaseMode::Fold => panic!("lowercase case folding mode is not yet implemented"),
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum UppercaseMode {
Full,
Ascii,
Turkic,
Lithuanian,
}
impl Default for UppercaseMode {
fn default() -> Self {
Self::Full
}
}
impl TryFrom<&str> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.as_bytes().try_into()
}
}
impl TryFrom<Option<&str>> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: Option<&str>) -> Result<Self, Self::Error> {
value.map(str::as_bytes).try_into()
}
}
impl TryFrom<&[u8]> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value {
b"ascii" => Ok(Self::Ascii),
b"turkic" => Ok(Self::Turkic),
b"lithuanian" => Ok(Self::Lithuanian),
_ => Err(InvalidCaseMappingMode::new()),
}
}
}
impl TryFrom<Option<&[u8]>> for UppercaseMode {
type Error = InvalidCaseMappingMode;
#[inline]
fn try_from(value: Option<&[u8]>) -> Result<Self, Self::Error> {
match value {
None => Ok(Self::Full),
Some(b"ascii") => Ok(Self::Ascii),
Some(b"turkic") => Ok(Self::Turkic),
Some(b"lithuanian") => Ok(Self::Lithuanian),
Some(_) => Err(InvalidCaseMappingMode::new()),
}
}
}
impl FromStr for UppercaseMode {
type Err = InvalidCaseMappingMode;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
pub fn uppercase(slice: &[u8], options: UppercaseMode) -> Uppercase<'_> {
match options {
UppercaseMode::Full | UppercaseMode::Lithuanian => Uppercase::with_slice(slice),
UppercaseMode::Ascii => Uppercase::with_ascii_slice(slice),
UppercaseMode::Turkic => panic!("uppercase Turkic mode is not yet implemented"),
}
}
#[cfg(doctest)]
macro_rules! readme {
($x:expr) => {
#[doc = $x]
mod readme {}
};
() => {
readme!(include_str!("../README.md"));
};
}
#[cfg(doctest)]
readme!();