1use std::iter;
2use std::io::Write;
3use std::string::FromUtf8Error;
4
5const DEFAULT_CAPACITY: usize = 1024;
6const MAX_UNICODE_WIDTH: usize = 4;
7
8#[derive(Debug)]
10pub struct Builder(Vec<u8>);
11
12impl Default for Builder {
13 fn default() -> Builder {
14 let inner = Vec::with_capacity(DEFAULT_CAPACITY);
15 Builder(inner)
16 }
17}
18
19impl Builder {
20 pub fn new(size: usize) -> Builder {
22 let inner = Vec::with_capacity(size);
23 Builder(inner)
24 }
25
26 pub fn append<T: ToBytes>(&mut self, buf: T) {
37 self.0.write_all(&buf.to_bytes()).unwrap()
38 }
39
40 pub fn len(&self) -> usize {
52 self.0.len()
53 }
54
55 pub fn string(self) -> Result<String, FromUtf8Error> {
70 String::from_utf8(self.0)
71 }
72}
73
74pub trait ToBytes {
76 fn to_bytes(&self) -> Vec<u8>;
77}
78
79fn make_copyable_buf(len: usize) -> Vec<u8> {
81 iter::repeat(0).take(len).collect::<Vec<u8>>()
82}
83
84fn slice_to_vec(s: &[u8]) -> Vec<u8> {
86 let mut res = make_copyable_buf(s.len());
87 res.copy_from_slice(s);
88 res
89}
90
91impl ToBytes for String {
92 fn to_bytes(&self) -> Vec<u8> {
93 slice_to_vec(self.as_bytes())
94 }
95}
96
97impl<'a> ToBytes for &'a str {
98 fn to_bytes(&self) -> Vec<u8> {
99 slice_to_vec(self.as_bytes())
100 }
101}
102
103impl ToBytes for u8 {
104 fn to_bytes(&self) -> Vec<u8> {
105 vec![*self]
106 }
107}
108
109impl ToBytes for char {
110 fn to_bytes(&self) -> Vec<u8> {
111 let mut buf = [0; MAX_UNICODE_WIDTH];
113 slice_to_vec(self.encode_utf8(&mut buf).as_bytes())
114 }
115}
116
117impl<'a> ToBytes for &'a [u8] {
118 fn to_bytes(&self) -> Vec<u8> {
119 slice_to_vec(self)
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::Builder;
126
127 #[test]
128 fn test_all_supported_types() {
129 let mut b = Builder::default();
130 b.append(String::from("hello"));
131 b.append(',');
132 b.append(b' ');
133 b.append("world");
134 b.append(" it works".as_bytes());
135
136 assert_eq!(b.string().unwrap(), "hello, world it works");
137 }
138
139 #[test]
140 fn test_individual_unicode_characters() {
141 let mut b = Builder::default();
142 b.append('‘');
143 b.append("starts with and ends with");
144 b.append('‗');
145
146 assert_eq!(b.string().unwrap(), "‘starts with and ends with‗");
147 }
148
149 #[test]
150 fn test_tool_album() {
151 let mut b = Builder::default();
152 b.append('\u{00C6}');
153 b.append("nima");
154
155 assert_eq!(b.string().unwrap(), "\u{00C6}nima");
156 }
157}