inline_str/
lib.rs

1// Copyright 2024 Adam Gutglick
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7// 	http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::str;
16use std::{borrow::Cow, ops::Deref};
17
18use inline_array::InlineArray;
19
20#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct InlineStr {
23    inner: InlineArray,
24}
25
26impl std::fmt::Display for InlineStr {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        std::fmt::Display::fmt(&**self, f)
29    }
30}
31
32impl std::fmt::Debug for InlineStr {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        std::fmt::Debug::fmt(&**self, f)
35    }
36}
37
38impl std::hash::Hash for InlineStr {
39    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
40        let as_str: &str = &*self;
41        as_str.hash(state);
42    }
43}
44
45impl From<String> for InlineStr {
46    fn from(value: String) -> Self {
47        Self {
48            inner: InlineArray::from(value.as_bytes()),
49        }
50    }
51}
52
53impl From<&String> for InlineStr {
54    fn from(value: &String) -> Self {
55        Self {
56            inner: InlineArray::from(value.as_bytes()),
57        }
58    }
59}
60
61impl From<&str> for InlineStr {
62    fn from(value: &str) -> Self {
63        Self {
64            inner: InlineArray::from(value.as_bytes()),
65        }
66    }
67}
68
69impl Deref for InlineStr {
70    type Target = str;
71
72    fn deref(&self) -> &Self::Target {
73        // Safety:
74        // InlineStr can only be created from valid UTF8 byte sequences
75        unsafe { str::from_utf8_unchecked(&self.inner) }
76    }
77}
78
79impl PartialEq<String> for InlineStr {
80    fn eq(&self, other: &String) -> bool {
81        (**self).eq(other)
82    }
83}
84
85impl PartialEq<InlineStr> for String {
86    fn eq(&self, other: &InlineStr) -> bool {
87        other.eq(self)
88    }
89}
90
91impl<'a> PartialEq<&'a str> for InlineStr {
92    fn eq(&self, other: &&'a str) -> bool {
93        (&&**self).eq(other)
94    }
95}
96
97impl PartialEq<InlineStr> for &str {
98    fn eq(&self, other: &InlineStr) -> bool {
99        other.eq(self)
100    }
101}
102
103impl PartialEq<Cow<'_, str>> for InlineStr {
104    fn eq(&self, other: &Cow<'_, str>) -> bool {
105        (**self).eq(other)
106    }
107}
108
109impl PartialEq<InlineStr> for Cow<'_, str> {
110    fn eq(&self, other: &InlineStr) -> bool {
111        other.eq(self)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use std::hash::{BuildHasher, RandomState};
118
119    use super::*;
120
121    #[test]
122    fn test_basic_eq() {
123        let words = "the quick brown fox";
124        let inline_words = InlineStr::from(words);
125
126        assert_eq!(words, &*inline_words);
127        assert_eq!(words, inline_words);
128        assert_eq!(inline_words, words);
129    }
130
131    #[test]
132    fn test_basic_hash() {
133        let hasher = RandomState::new();
134
135        let words = "the quick brown fox";
136        let inline_words = InlineStr::from(words);
137
138        let words_hash = hasher.hash_one(words);
139        let words_hash_2 = hasher.hash_one(words);
140        let inline_hash = hasher.hash_one(inline_words);
141
142        assert_eq!(words_hash, words_hash_2);
143        assert_eq!(words_hash, inline_hash);
144    }
145}