rama_core/username/
compose.rs

1use rama_utils::macros::all_the_tuples_no_last_special_case;
2use std::{
3    fmt::{self, Write},
4    sync::Arc,
5};
6
7use super::DEFAULT_USERNAME_LABEL_SEPARATOR;
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/// [`std::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 std::error::Error for ComposeError {
38    fn source(&self) -> Option<&(dyn std::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    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
106        composer.write_label(self)
107    }
108}
109
110impl<const SEPARATOR: char, const N: usize, W> UsernameLabelWriter<SEPARATOR> for [W; N]
111where
112    W: UsernameLabelWriter<SEPARATOR>,
113{
114    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
115        for writer in self {
116            writer.write_labels(composer)?;
117        }
118        Ok(())
119    }
120}
121
122impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for Option<W>
123where
124    W: UsernameLabelWriter<SEPARATOR>,
125{
126    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
127        match self {
128            Some(writer) => writer.write_labels(composer),
129            None => Ok(()),
130        }
131    }
132}
133
134impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for &W
135where
136    W: UsernameLabelWriter<SEPARATOR>,
137{
138    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
139        (*self).write_labels(composer)
140    }
141}
142
143impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for Arc<W>
144where
145    W: UsernameLabelWriter<SEPARATOR>,
146{
147    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
148        (**self).write_labels(composer)
149    }
150}
151
152impl<const SEPARATOR: char, W> UsernameLabelWriter<SEPARATOR> for Vec<W>
153where
154    W: UsernameLabelWriter<SEPARATOR>,
155{
156    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
157        for writer in self {
158            writer.write_labels(composer)?;
159        }
160        Ok(())
161    }
162}
163
164macro_rules! impl_username_label_writer_either {
165    ($id:ident, $($param:ident),+ $(,)?) => {
166        impl<const SEPARATOR: char, $($param),+> UsernameLabelWriter<SEPARATOR> for crate::combinators::$id<$($param),+>
167        where
168            $(
169                $param: UsernameLabelWriter<SEPARATOR>,
170            )+
171        {
172            fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
173                match self {
174                    $(
175                        crate::combinators::$id::$param(writer) => {
176                            writer.write_labels(composer)
177                        }
178                    )+
179                }
180            }
181        }
182    };
183}
184
185crate::combinators::impl_either!(impl_username_label_writer_either);
186
187macro_rules! impl_username_label_writer_for_tuple {
188    ( $($ty:ident),* $(,)? ) => {
189        #[allow(non_snake_case)]
190        impl<const SEPARATOR: char, $($ty),*> UsernameLabelWriter<SEPARATOR> for ($($ty,)*)
191        where
192            $( $ty: UsernameLabelWriter<SEPARATOR>, )*
193        {
194            fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
195                let ($($ty),*,) = self;
196                $(
197                    $ty.write_labels(composer)?;
198                )*
199                Ok(())
200            }
201        }
202    };
203}
204all_the_tuples_no_last_special_case!(impl_username_label_writer_for_tuple);