yazi_shared/
kebab_cased_string.rs1use std::{borrow::{Borrow, Cow}, ffi::OsStr, fmt::{Display, Formatter}, ops::Deref};
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::{BytesExt, SnakeCasedString};
6
7#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
8#[serde(transparent)]
9pub struct KebabCasedString(String);
10
11impl KebabCasedString {
12 pub fn new(s: String) -> Option<Self> { s.as_bytes().kebab_cased().then_some(Self(s)) }
13
14 pub fn into_snake_cased(self) -> SnakeCasedString {
15 let mut b = self.0.into_bytes();
16 b.iter_mut().for_each(|c| {
17 if *c == b'-' {
18 *c = b'_'
19 }
20 });
21 SnakeCasedString(unsafe { String::from_utf8_unchecked(b) })
22 }
23}
24
25impl Deref for KebabCasedString {
26 type Target = str;
27
28 #[inline]
29 fn deref(&self) -> &Self::Target { &self.0 }
30}
31
32impl Borrow<str> for KebabCasedString {
33 #[inline]
34 fn borrow(&self) -> &str { &self.0 }
35}
36
37impl Borrow<String> for KebabCasedString {
38 #[inline]
39 fn borrow(&self) -> &String { &self.0 }
40}
41
42impl AsRef<str> for KebabCasedString {
43 #[inline]
44 fn as_ref(&self) -> &str { &self.0 }
45}
46
47impl AsRef<OsStr> for KebabCasedString {
48 #[inline]
49 fn as_ref(&self) -> &OsStr { self.0.as_ref() }
50}
51
52impl Display for KebabCasedString {
53 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.0, f) }
54}
55
56impl From<KebabCasedString> for String {
57 #[inline]
58 fn from(value: KebabCasedString) -> Self { value.0 }
59}
60
61impl From<KebabCasedString> for Cow<'_, str> {
62 #[inline]
63 fn from(value: KebabCasedString) -> Self { Cow::Owned(value.0) }
64}
65
66impl<'de> Deserialize<'de> for KebabCasedString {
67 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
68 let value = String::deserialize(deserializer)?;
69 Self::new(value).ok_or_else(|| serde::de::Error::custom("must be a kebab-cased string"))
70 }
71}