1use std::fmt::{Display, Write};
2
3use serde::{Deserialize, Serialize};
4
5use crate::document::{BorrowedDocument, CollectionDocument, DocumentId, OwnedDocument, Revision};
6use crate::key::Key;
7use crate::schema::view::map::Mappings;
8use crate::schema::{Map, SerializedCollection};
9
10#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
12pub struct Header {
13 pub id: DocumentId,
16
17 pub revision: Revision,
19}
20
21pub trait HasHeader {
23 fn header(&self) -> Result<Header, crate::Error>;
25}
26
27impl HasHeader for Header {
28 fn header(&self) -> Result<Header, crate::Error> {
29 Ok(self.clone())
30 }
31}
32
33pub trait Emit {
36 fn emit(&self) -> Result<Mappings<(), ()>, crate::Error> {
38 self.emit_key_and_value((), ())
39 }
40
41 fn emit_if(&self, condition: bool) -> Result<Mappings<(), ()>, crate::Error> {
44 if condition {
45 self.emit()
46 } else {
47 Ok(Mappings::default())
48 }
49 }
50
51 fn emit_key<K>(&self, key: K) -> Result<Mappings<K, ()>, crate::Error> {
53 self.emit_key_and_value(key, ())
54 }
55
56 fn emit_value<Value>(&self, value: Value) -> Result<Mappings<(), Value>, crate::Error> {
58 self.emit_key_and_value((), value)
59 }
60
61 fn emit_key_and_value<K, Value>(
63 &self,
64 key: K,
65 value: Value,
66 ) -> Result<Mappings<K, Value>, crate::Error>;
67}
68
69impl Emit for Header {
70 fn emit_key_and_value<K, Value>(
71 &self,
72 key: K,
73 value: Value,
74 ) -> Result<Mappings<K, Value>, crate::Error> {
75 Ok(Mappings::Simple(Some(Map::new(self.clone(), key, value))))
76 }
77}
78
79impl Display for Header {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 self.id.fmt(f)?;
82 f.write_char('@')?;
83 self.revision.fmt(f)
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
89pub struct CollectionHeader<PrimaryKey> {
90 pub id: PrimaryKey,
92 pub revision: Revision,
94}
95
96impl<PrimaryKey> Emit for CollectionHeader<PrimaryKey>
97where
98 PrimaryKey: for<'k> Key<'k>,
99{
100 fn emit_key_and_value<K, Value>(
101 &self,
102 key: K,
103 value: Value,
104 ) -> Result<Mappings<K, Value>, crate::Error> {
105 let header = Header::try_from(self.clone())?;
106 Ok(Mappings::Simple(Some(Map::new(header, key, value))))
107 }
108}
109
110impl<PrimaryKey> HasHeader for CollectionHeader<PrimaryKey>
111where
112 PrimaryKey: for<'k> Key<'k>,
113{
114 fn header(&self) -> Result<Header, crate::Error> {
115 Header::try_from(self.clone())
116 }
117}
118
119impl HasHeader for OwnedDocument {
120 fn header(&self) -> Result<Header, crate::Error> {
121 self.header.header()
122 }
123}
124
125impl<'a> HasHeader for BorrowedDocument<'a> {
126 fn header(&self) -> Result<Header, crate::Error> {
127 self.header.header()
128 }
129}
130
131impl<C> HasHeader for CollectionDocument<C>
132where
133 C: SerializedCollection,
134{
135 fn header(&self) -> Result<Header, crate::Error> {
136 self.header.header()
137 }
138}
139
140impl<PrimaryKey> TryFrom<Header> for CollectionHeader<PrimaryKey>
141where
142 PrimaryKey: for<'k> Key<'k>,
143{
144 type Error = crate::Error;
145
146 fn try_from(value: Header) -> Result<Self, Self::Error> {
147 Ok(Self {
148 id: value.id.deserialize::<PrimaryKey>()?,
149 revision: value.revision,
150 })
151 }
152}
153
154impl<'a, PrimaryKey> TryFrom<&'a Header> for CollectionHeader<PrimaryKey>
155where
156 PrimaryKey: for<'k> Key<'k>,
157{
158 type Error = crate::Error;
159
160 fn try_from(value: &'a Header) -> Result<Self, Self::Error> {
161 Ok(Self {
162 id: value.id.deserialize::<PrimaryKey>()?,
163 revision: value.revision,
164 })
165 }
166}
167
168impl<PrimaryKey> TryFrom<CollectionHeader<PrimaryKey>> for Header
169where
170 PrimaryKey: for<'k> Key<'k>,
171{
172 type Error = crate::Error;
173
174 fn try_from(value: CollectionHeader<PrimaryKey>) -> Result<Self, Self::Error> {
175 Ok(Self {
176 id: DocumentId::new(&value.id)?,
177 revision: value.revision,
178 })
179 }
180}
181
182impl<'a, PrimaryKey> TryFrom<&'a CollectionHeader<PrimaryKey>> for Header
183where
184 PrimaryKey: for<'k> Key<'k>,
185{
186 type Error = crate::Error;
187
188 fn try_from(value: &'a CollectionHeader<PrimaryKey>) -> Result<Self, Self::Error> {
189 Ok(Self {
190 id: DocumentId::new(&value.id)?,
191 revision: value.revision,
192 })
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
198pub enum AnyHeader<PrimaryKey> {
199 Serialized(Header),
201 Collection(CollectionHeader<PrimaryKey>),
203}
204
205impl<PrimaryKey> AnyHeader<PrimaryKey>
206where
207 PrimaryKey: for<'k> Key<'k>,
208{
209 pub fn into_header(self) -> Result<Header, crate::Error> {
211 match self {
212 AnyHeader::Serialized(header) => Ok(header),
213 AnyHeader::Collection(header) => Header::try_from(header),
214 }
215 }
216}
217
218#[test]
219fn emissions_tests() -> Result<(), crate::Error> {
220 use crate::schema::Map;
221 use crate::test_util::Basic;
222
223 let doc = BorrowedDocument::with_contents::<Basic, _>(&1, &Basic::default())?;
224
225 assert_eq!(
226 doc.header.emit()?,
227 Mappings::Simple(Some(Map::new(doc.header.clone(), (), ())))
228 );
229
230 assert_eq!(
231 doc.header.emit_key(1)?,
232 Mappings::Simple(Some(Map::new(doc.header.clone(), 1, ())))
233 );
234
235 assert_eq!(
236 doc.header.emit_value(1)?,
237 Mappings::Simple(Some(Map::new(doc.header.clone(), (), 1)))
238 );
239
240 assert_eq!(
241 doc.header.emit_key_and_value(1, 2)?,
242 Mappings::Simple(Some(Map::new(doc.header, 1, 2)))
243 );
244
245 Ok(())
246}
247
248#[test]
249fn chained_mappings_test() -> Result<(), crate::Error> {
250 use crate::schema::Map;
251 use crate::test_util::Basic;
252
253 let doc = BorrowedDocument::with_contents::<Basic, _>(&1, &Basic::default())?;
254
255 assert_eq!(
256 doc.header.emit()?.and(doc.header.emit()?),
257 Mappings::List(vec![
258 Map::new(doc.header.clone(), (), ()),
259 Map::new(doc.header, (), ())
260 ])
261 );
262
263 Ok(())
264}
265
266#[test]
267fn header_display_test() {
268 let original_contents = b"one";
269 let revision = Revision::new(original_contents);
270 let header = Header {
271 id: DocumentId::new(&42_u64).unwrap(),
272 revision,
273 };
274 assert_eq!(
275 header.to_string(),
276 "7$2a@0-7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed"
277 );
278}