Skip to main content

rama_core/username/
compose.rs

1use core::fmt::{self, Write};
2
3use crate::std::{string::String, sync::Arc, vec::Vec};
4
5use super::DEFAULT_USERNAME_LABEL_SEPARATOR;
6
7use rama_utils::macros::all_the_tuples_no_last_special_case;
8
9#[derive(Debug, Clone)]
10/// Composer struct used to compose a username into a [`String`],
11/// with labels, all separated by the given `SEPARATOR`. Empty labels
12/// aren't allowed.
13pub struct Composer<const SEPARATOR: char = DEFAULT_USERNAME_LABEL_SEPARATOR> {
14    buffer: String,
15}
16
17#[derive(Debug, Clone)]
18/// [`core::error::Error`] returned in case composing of a username,
19/// using [`Composer`] went wrong, somehow.
20pub struct ComposeError(ComposeErrorKind);
21
22#[derive(Debug, Clone)]
23enum ComposeErrorKind {
24    EmptyLabel,
25    FmtError(fmt::Error),
26}
27
28impl fmt::Display for ComposeError {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self.0 {
31            ComposeErrorKind::EmptyLabel => f.write_str("empty label"),
32            ComposeErrorKind::FmtError(err) => write!(f, "fmt error: {err}"),
33        }
34    }
35}
36
37impl core::error::Error for ComposeError {
38    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
39        match &self.0 {
40            ComposeErrorKind::EmptyLabel => None,
41            ComposeErrorKind::FmtError(err) => err.source(),
42        }
43    }
44}
45
46impl<const SEPARATOR: char> Composer<SEPARATOR> {
47    fn new(username: String) -> Self {
48        Self { buffer: username }
49    }
50
51    /// write a label into the [`Composer`].
52    pub fn write_label(&mut self, label: impl AsRef<str>) -> Result<(), ComposeError> {
53        self.buffer
54            .write_char(SEPARATOR)
55            .map_err(|err| ComposeError(ComposeErrorKind::FmtError(err)))?;
56        let label = label.as_ref();
57        if label.is_empty() {
58            return Err(ComposeError(ComposeErrorKind::EmptyLabel));
59        }
60        self.buffer
61            .write_str(label.as_ref())
62            .map_err(|err| ComposeError(ComposeErrorKind::FmtError(err)))?;
63        Ok(())
64    }
65
66    fn compose(self) -> String {
67        self.buffer
68    }
69}
70
71#[inline]
72/// Compose a username into a username together with its labels.
73pub fn compose_username(
74    username: String,
75    labels: impl UsernameLabelWriter<DEFAULT_USERNAME_LABEL_SEPARATOR>,
76) -> Result<String, ComposeError> {
77    compose_username_with_separator::<DEFAULT_USERNAME_LABEL_SEPARATOR>(username, labels)
78}
79
80/// Compose a username into a username together with its labels,
81/// using a custom separator instead of the default ([`DEFAULT_USERNAME_LABEL_SEPARATOR`])
82pub fn compose_username_with_separator<const SEPARATOR: char>(
83    username: String,
84    labels: impl UsernameLabelWriter<SEPARATOR>,
85) -> Result<String, ComposeError> {
86    let mut composer = Composer::<SEPARATOR>::new(username);
87    labels.write_labels(&mut composer)?;
88    Ok(composer.compose())
89}
90
91/// A type that can write itself as label(s) to compose into a
92/// username with labels. Often used by passing it to [`compose_username`].
93pub trait UsernameLabelWriter<const SEPARATOR: char> {
94    /// Write all labels into the given [`Composer`].
95    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError>;
96}
97
98impl<const SEPARATOR: char> UsernameLabelWriter<SEPARATOR> for String {
99    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
100        composer.write_label(self)
101    }
102}
103
104impl<const SEPARATOR: char> UsernameLabelWriter<SEPARATOR> for &str {
105    #[inline(always)]
106    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
107        composer.write_label(self)
108    }
109}
110
111impl<const SEPARATOR: char, const N: usize, W> UsernameLabelWriter<SEPARATOR> for [W; N]
112where
113    W: UsernameLabelWriter<SEPARATOR>,
114{
115    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
116        for writer in self {
117            writer.write_labels(composer)?;
118        }
119        Ok(())
120    }
121}
122
123impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for Option<W>
124where
125    W: UsernameLabelWriter<SEPARATOR>,
126{
127    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
128        match self {
129            Some(writer) => writer.write_labels(composer),
130            None => Ok(()),
131        }
132    }
133}
134
135impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for &W
136where
137    W: UsernameLabelWriter<SEPARATOR>,
138{
139    #[inline(always)]
140    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
141        (*self).write_labels(composer)
142    }
143}
144
145impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for Arc<W>
146where
147    W: UsernameLabelWriter<SEPARATOR>,
148{
149    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
150        (**self).write_labels(composer)
151    }
152}
153
154impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for Vec<W>
155where
156    W: UsernameLabelWriter<SEPARATOR>,
157{
158    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
159        for writer in self {
160            writer.write_labels(composer)?;
161        }
162        Ok(())
163    }
164}
165
166macro_rules! impl_username_label_writer_either {
167    ($id:ident, $($param:ident),+ $(,)?) => {
168        impl<const SEPARATOR: char, $($param),+> UsernameLabelWriter<SEPARATOR> for crate::combinators::$id<$($param),+>
169        where
170            $(
171                $param: UsernameLabelWriter<SEPARATOR>,
172            )+
173        {
174            fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
175                match self {
176                    $(
177                        crate::combinators::$id::$param(writer) => {
178                            writer.write_labels(composer)
179                        }
180                    )+
181                }
182            }
183        }
184    };
185}
186
187crate::combinators::impl_either!(impl_username_label_writer_either);
188
189macro_rules! impl_username_label_writer_for_tuple {
190    ( $($ty:ident),* $(,)? ) => {
191        impl<const SEPARATOR: char, $($ty),*> UsernameLabelWriter<SEPARATOR> for ($($ty,)*)
192        where
193            $( $ty: UsernameLabelWriter<SEPARATOR>, )*
194        {
195            fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
196                let ($($ty),*,) = self;
197                $(
198                    $ty.write_labels(composer)?;
199                )*
200                Ok(())
201            }
202        }
203    };
204}
205all_the_tuples_no_last_special_case!(impl_username_label_writer_for_tuple);