1use crate::{Header, Length, Result, SliceWriter, Tagged, Writer};
4
5#[cfg(feature = "alloc")]
6use {alloc::vec::Vec, core::iter};
7
8#[cfg(feature = "pem")]
9use {
10 crate::PemWriter,
11 alloc::string::String,
12 pem_rfc7468::{self as pem, LineEnding, PemLabel},
13};
14
15#[cfg(any(feature = "alloc", feature = "pem"))]
16use crate::ErrorKind;
17
18#[cfg(doc)]
19use crate::Tag;
20
21pub trait Encode {
23 fn encoded_len(&self) -> Result<Length>;
25
26 fn encode(&self, encoder: &mut dyn Writer) -> Result<()>;
28
29 fn encode_to_slice<'a>(&self, buf: &'a mut [u8]) -> Result<&'a [u8]> {
32 let mut writer = SliceWriter::new(buf);
33 self.encode(&mut writer)?;
34 writer.finish()
35 }
36
37 #[cfg(feature = "alloc")]
40 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
41 fn encode_to_vec(&self, buf: &mut Vec<u8>) -> Result<Length> {
42 let expected_len = usize::try_from(self.encoded_len()?)?;
43 buf.reserve(expected_len);
44 buf.extend(iter::repeat(0).take(expected_len));
45
46 let mut writer = SliceWriter::new(buf);
47 self.encode(&mut writer)?;
48 let actual_len = writer.finish()?.len();
49
50 if expected_len != actual_len {
51 return Err(ErrorKind::Incomplete {
52 expected_len: expected_len.try_into()?,
53 actual_len: actual_len.try_into()?,
54 }
55 .into());
56 }
57
58 actual_len.try_into()
59 }
60
61 #[cfg(feature = "alloc")]
63 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
64 fn to_vec(&self) -> Result<Vec<u8>> {
65 let mut buf = Vec::new();
66 self.encode_to_vec(&mut buf)?;
67 Ok(buf)
68 }
69}
70
71impl<T> Encode for T
72where
73 T: EncodeValue + Tagged,
74{
75 fn encoded_len(&self) -> Result<Length> {
77 self.value_len().and_then(|len| len.for_tlv())
78 }
79
80 fn encode(&self, writer: &mut dyn Writer) -> Result<()> {
82 self.header()?.encode(writer)?;
83 self.encode_value(writer)
84 }
85}
86
87#[cfg(feature = "pem")]
92#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
93pub trait EncodePem: Encode + PemLabel {
94 fn to_pem(&self, line_ending: LineEnding) -> Result<String>;
96}
97
98#[cfg(feature = "pem")]
99#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
100impl<T: Encode + PemLabel> EncodePem for T {
101 fn to_pem(&self, line_ending: LineEnding) -> Result<String> {
102 let der_len = usize::try_from(self.encoded_len()?)?;
103 let pem_len = pem::encapsulated_len(Self::PEM_LABEL, line_ending, der_len)?;
104
105 let mut buf = vec![0u8; pem_len];
106 let mut writer = PemWriter::new(Self::PEM_LABEL, line_ending, &mut buf)?;
107 self.encode(&mut writer)?;
108
109 let actual_len = writer.finish()?;
110 buf.truncate(actual_len);
111 Ok(String::from_utf8(buf)?)
112 }
113}
114
115pub trait EncodeValue {
118 fn header(&self) -> Result<Header>
120 where
121 Self: Tagged,
122 {
123 Header::new(self.tag(), self.value_len()?)
124 }
125
126 fn value_len(&self) -> Result<Length>;
129
130 fn encode_value(&self, encoder: &mut dyn Writer) -> Result<()>;
133}