capitalize/lib.rs
1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(feature = "nightly", feature(iter_intersperse))]
4#![cfg_attr(feature = "nightly", feature(doc_cfg))]
5
6// NOTE: Why mainly use iterator methods? In the end we will copy each char,
7// doing it in that way we avoid use indexing (a[b]). To avoid compiler check
8// bounds and possible panic. It must be compared with benchmarks to ensure
9// that speed is not affected or optimize that.
10
11mod iter;
12use iter::CapitalizeIterator;
13
14/// It's implemented for all types that implement [`AsRef<str>`].
15pub trait Capitalize: AsRef<str> {
16 /// First character to title case and the rest to lower case.
17 /// This means that characters like digraphs will only have
18 /// their first letter capitalized, instead of the full character.
19 ///
20 /// Behavior is like [Python's `str.capitalize`]. Also, it uses
21 /// [`char::to_uppercase`] under the hood, then read its doc.
22 /// That relies on Unicode to change to upper case.
23 ///
24 /// # Examples
25 ///
26 /// ```rust
27 /// # use capitalize::Capitalize;
28 /// assert_eq!("✨ Hello World".capitalize(), "✨ hello world");
29 /// assert_eq!("ñandu".capitalize(), "Ñandu");
30 /// assert_eq!("こんにちは世界".capitalize(), "こんにちは世界");
31 /// ```
32 ///
33 /// [Python's `str.capitalize`]: https://docs.python.org/3/library/stdtypes.html#str.capitalize
34 fn capitalize(&self) -> String;
35
36 /// Split by space into words, then for each word change the first
37 /// character to title case and the rest to lower case.
38 /// This means that characters like digraphs will only have
39 /// their first letter capitalized, instead of the full character.
40 ///
41 /// It uses [`char.to_uppercase()`] under the hood, then read its doc.
42 /// That relies on Unicode to change to upper case.
43 ///
44 /// # Examples
45 ///
46 /// ```rust
47 /// # use capitalize::Capitalize;
48 /// assert_eq!("✨ hello world".capitalize_words(), "✨ Hello World");
49 /// assert_eq!("ñandu".capitalize_words(), "Ñandu");
50 /// assert_eq!("こんにちは世界".capitalize_words(), "こんにちは世界");
51 /// ```
52 #[cfg(feature = "nightly")]
53 #[doc(cfg(feature = "nightly"))]
54 fn capitalize_words(&self) -> String;
55
56 /// First character to upper case and the rest will remain the same.
57 ///
58 /// It uses [`char.to_uppercase()`] under the hood, then read its doc.
59 /// That relies on Unicode to change to upper case.
60 ///
61 /// # Examples
62 ///
63 /// ```rust
64 /// # use capitalize::Capitalize;
65 /// assert_eq!("hello World".capitalize_first_only(), "Hello World");
66 /// assert_eq!("✨ hello World".capitalize_first_only(), "✨ hello World");
67 /// ```
68 fn capitalize_first_only(&self) -> String;
69
70 /// The last character to upper case and the rest will remain the same.
71 ///
72 /// It uses [`char.to_uppercase()`] under the hood, then read its doc.
73 /// That relies on Unicode to change to upper case.
74 ///
75 /// # Examples
76 ///
77 /// ```rust
78 /// # use capitalize::Capitalize;
79 /// assert_eq!("✨ Hello World".capitalize_last_only(), "✨ Hello WorlD");
80 /// assert_eq!("Hello World ✨".capitalize_last_only(), "Hello World ✨");
81 /// assert_eq!("hello world".capitalize_last_only(), "hello worlD");
82 /// ```
83 fn capitalize_last_only(&self) -> String;
84}
85
86impl<T: AsRef<str>> Capitalize for T {
87 fn capitalize(&self) -> String {
88 let string = self.as_ref();
89
90 let mut buf = String::with_capacity(string.len());
91 buf.extend(string.chars().capitalize());
92
93 return buf;
94 }
95
96 #[cfg(feature = "nightly")]
97 fn capitalize_words(&self) -> String {
98 if self.as_ref().is_empty() {
99 return String::with_capacity(0);
100 }
101 self.as_ref()
102 .split(" ")
103 .intersperse(" ")
104 .map(|item| item.chars().capitalize())
105 .flatten()
106 .collect()
107 }
108
109 fn capitalize_first_only(&self) -> String {
110 let mut chars = self.as_ref().chars();
111 let Some(first) = chars.next() else {
112 return String::with_capacity(0);
113 };
114 first.to_uppercase().chain(chars).collect()
115 }
116
117 fn capitalize_last_only(&self) -> String {
118 let mut chars = self.as_ref().chars().rev();
119 let Some(last) = chars.next() else {
120 return String::with_capacity(0);
121 };
122 last.to_uppercase().chain(chars).rev().collect()
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use super::Capitalize;
129
130 #[test]
131 fn string_reference() {
132 let text = String::from("hello ✨ World");
133 let text_ref = &text;
134 assert_eq!(text_ref.capitalize(), "Hello ✨ world");
135 }
136
137 #[test]
138 fn capitalize_first_only_reference() {
139 let text = String::from("heLLo ✨ World");
140 let text_ref = &text;
141 assert_eq!(text_ref.capitalize_first_only(), "HeLLo ✨ World");
142 }
143
144 #[test]
145 fn capitalize_final_only_reference() {
146 let text = String::from("heLLo ✨ World");
147 let text_ref = &text;
148 assert_eq!(text_ref.capitalize_last_only(), "heLLo ✨ WorlD");
149 }
150}