use crate::{checked::CheckedSum, writer::Writer, Error};
use core::str;
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
#[cfg(feature = "pem")]
use {
crate::PEM_LINE_WIDTH,
pem::{LineEnding, PemLabel},
};
pub trait Encode {
type Error: From<Error>;
fn encoded_len(&self) -> Result<usize, Self::Error>;
fn encode(&self, writer: &mut impl Writer) -> Result<(), Self::Error>;
fn encoded_len_prefixed(&self) -> Result<usize, Self::Error> {
Ok([4, self.encoded_len()?].checked_sum()?)
}
fn encode_prefixed(&self, writer: &mut impl Writer) -> Result<(), Self::Error> {
self.encoded_len()?.encode(writer)?;
self.encode(writer)
}
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
pub trait EncodePem: Encode + PemLabel {
fn encode_pem<'o>(
&self,
line_ending: LineEnding,
out: &'o mut [u8],
) -> Result<&'o str, Self::Error>;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn encode_pem_string(&self, line_ending: LineEnding) -> Result<String, Self::Error>;
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
impl<T: Encode + PemLabel> EncodePem for T {
fn encode_pem<'o>(
&self,
line_ending: LineEnding,
out: &'o mut [u8],
) -> Result<&'o str, Self::Error> {
let mut writer =
pem::Encoder::new_wrapped(Self::PEM_LABEL, PEM_LINE_WIDTH, line_ending, out)
.map_err(Error::from)?;
self.encode(&mut writer)?;
let encoded_len = writer.finish().map_err(Error::from)?;
Ok(str::from_utf8(&out[..encoded_len]).map_err(Error::from)?)
}
#[cfg(feature = "alloc")]
fn encode_pem_string(&self, line_ending: LineEnding) -> Result<String, Self::Error> {
let encoded_len = pem::encapsulated_len_wrapped(
Self::PEM_LABEL,
PEM_LINE_WIDTH,
line_ending,
self.encoded_len()?,
)
.map_err(Error::from)?;
let mut buf = vec![0u8; encoded_len];
let actual_len = self.encode_pem(line_ending, &mut buf)?.len();
buf.truncate(actual_len);
Ok(String::from_utf8(buf).map_err(Error::from)?)
}
}
impl Encode for u8 {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
Ok(1)
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
writer.write(&[*self])
}
}
impl Encode for u32 {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
Ok(4)
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
writer.write(&self.to_be_bytes())
}
}
impl Encode for u64 {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
Ok(8)
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
writer.write(&self.to_be_bytes())
}
}
impl Encode for usize {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
Ok(4)
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
u32::try_from(*self)?.encode(writer)
}
}
impl Encode for [u8] {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
[4, self.len()].checked_sum()
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.len().encode(writer)?;
writer.write(self)
}
}
impl<const N: usize> Encode for [u8; N] {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
self.as_slice().encoded_len()
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.as_slice().encode(writer)
}
}
impl Encode for &str {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
self.as_bytes().encoded_len()
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.as_bytes().encode(writer)
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl Encode for Vec<u8> {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
self.as_slice().encoded_len()
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.as_slice().encode(writer)
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl Encode for String {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
self.as_str().encoded_len()
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.as_str().encode(writer)
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl Encode for Vec<String> {
type Error = Error;
fn encoded_len(&self) -> Result<usize, Error> {
self.iter().try_fold(4usize, |acc, string| {
acc.checked_add(string.encoded_len()?).ok_or(Error::Length)
})
}
fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.encoded_len()?
.checked_sub(4)
.ok_or(Error::Length)?
.encode(writer)?;
for entry in self {
entry.encode(writer)?;
}
Ok(())
}
}