use std::array;
use std::borrow::Cow;
use super::encoding::encode;
use super::error::{Error, Result};
use super::path::validate;
use super::Format;
#[derive(Clone, Debug)]
pub struct Builder<'a, const N: usize> {
source: Option<&'a Format<N>>,
values: [Option<Cow<'a, str>>; N],
}
impl<const N: usize> Format<N> {
#[inline]
#[must_use]
pub fn builder<'a>() -> Builder<'a, N> {
Builder {
source: None,
values: [const { None }; N],
}
}
}
impl<'a, const N: usize> Builder<'a, N> {
#[inline]
#[must_use]
pub fn with<S>(mut self, index: usize, value: S) -> Self
where
S: Into<Cow<'a, str>>,
{
self.set(index, value);
self
}
#[inline]
pub fn set<S>(&mut self, index: usize, value: S) -> &mut Self
where
S: Into<Cow<'a, str>>,
{
self.values[index] = Some(value.into());
self
}
pub fn build(self) -> Result<Format<N>> {
let mut spans = array::from_fn(|_| 0u16..0u16);
let mut flags = 0;
let capacity = self.source.map_or(64, |format| format.value.len());
let mut buffer = Vec::with_capacity(capacity);
for (index, opt) in self.values.into_iter().enumerate() {
if index > 0 {
buffer.push(b':');
}
let start =
u16::try_from(buffer.len()).map_err(|_| Error::Overflow)?;
if let (None, Some(format)) = (opt.as_ref(), self.source) {
let p = format.spans[index].start as usize;
let q = format.spans[index].end as usize;
buffer.extend_from_slice(&format.value[p..q]);
let end =
u16::try_from(buffer.len()).map_err(|_| Error::Overflow)?;
spans[index] = start..end;
flags |= format.flags & (1 << index);
continue;
}
let Some(value) = opt else {
spans[index] = start..start;
continue;
};
let value = encode(value.as_bytes());
match value {
Cow::Borrowed(_) => flags &= !(1 << index),
Cow::Owned(_) => flags |= 1 << index,
}
validate(&value)?;
buffer.extend_from_slice(value.as_bytes());
let end =
u16::try_from(buffer.len()).map_err(|_| Error::Overflow)?;
spans[index] = start..end;
}
Ok(Format {
value: buffer.into(),
spans,
flags,
})
}
}
impl<'a, const N: usize> From<&'a Format<N>> for Builder<'a, N> {
#[inline]
fn from(format: &'a Format<N>) -> Self {
Self {
source: Some(format),
values: [const { None }; N],
}
}
}
impl<const N: usize> Default for Builder<'_, N> {
#[inline]
fn default() -> Self {
Format::builder()
}
}