icu_provider/export/
payload.rs1use core::any::Any;
6
7use crate::prelude::*;
8use crate::ule::MaybeEncodeAsVarULE;
9use crate::{dynutil::UpcastDataPayload, ule::MaybeAsVarULE};
10use alloc::sync::Arc;
11use databake::{Bake, BakeSize, CrateEnv, TokenStream};
12use yoke::*;
13use zerovec::VarZeroVec;
14use zerovec::vecs::Index32;
15
16#[cfg(doc)]
17use zerovec::ule::VarULE;
18
19trait ExportableDataPayload {
20 fn bake_yoke(&self, ctx: &CrateEnv) -> TokenStream;
21 fn bake_size(&self) -> usize;
22 fn serialize_yoke(
23 &self,
24 serializer: &mut dyn erased_serde::Serializer,
25 ) -> Result<(), DataError>;
26 fn maybe_bake_varule_encoded(
27 &self,
28 rest: &[&DataPayload<ExportMarker>],
29 ctx: &CrateEnv,
30 ) -> Option<TokenStream>;
31 fn as_any(&self) -> &dyn Any;
32 fn eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool;
33}
34
35impl<M: DynamicDataMarker> ExportableDataPayload for DataPayload<M>
36where
37 for<'a> <M::DataStruct as Yokeable<'a>>::Output:
38 Bake + BakeSize + serde::Serialize + MaybeEncodeAsVarULE + PartialEq,
39{
40 fn bake_yoke(&self, ctx: &CrateEnv) -> TokenStream {
41 self.get().bake(ctx)
42 }
43
44 fn bake_size(&self) -> usize {
45 size_of::<<M::DataStruct as Yokeable>::Output>() + self.get().borrows_size()
46 }
47
48 fn serialize_yoke(
49 &self,
50 serializer: &mut dyn erased_serde::Serializer,
51 ) -> Result<(), DataError> {
52 use erased_serde::Serialize;
53 self.get()
54 .erased_serialize(serializer)
55 .map_err(|e| DataError::custom("Serde export").with_display_context(&e))?;
56 Ok(())
57 }
58
59 fn maybe_bake_varule_encoded(
60 &self,
61 rest: &[&DataPayload<ExportMarker>],
62 ctx: &CrateEnv,
63 ) -> Option<TokenStream> {
64 let first_varule = self.get().maybe_as_encodeable()?;
65 let recovered_vec: Vec<
66 <<M::DataStruct as Yokeable<'_>>::Output as MaybeEncodeAsVarULE>::EncodeableStruct<'_>,
67 > = core::iter::once(first_varule)
68 .chain(rest.iter().map(|v| {
69 #[expect(clippy::expect_used)] v.get()
71 .payload
72 .as_any()
73 .downcast_ref::<Self>()
74 .expect("payloads expected to be same type")
75 .get()
76 .maybe_as_encodeable()
77 .expect("MaybeEncodeAsVarULE impl should be symmetric")
78 }))
79 .collect();
80 let vzv: VarZeroVec<
81 <<M::DataStruct as Yokeable<'_>>::Output as MaybeAsVarULE>::EncodedStruct,
82 Index32,
83 > = VarZeroVec::from(&recovered_vec);
84 let vzs = vzv.as_slice();
85 Some(vzs.bake(ctx))
86 }
87
88 fn as_any(&self) -> &dyn Any {
89 self
90 }
91
92 fn eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool {
93 match other.as_any().downcast_ref::<Self>() {
94 Some(downcasted) => (*self).eq(downcasted),
95 None => {
96 debug_assert!(
97 false,
98 "cannot compare ExportableDataPayloads of different types: self is {:?} but other is {:?}",
99 self.type_id(),
100 other.as_any().type_id(),
101 );
102 false
103 }
104 }
105 }
106}
107
108#[derive(yoke::Yokeable, Clone)]
109#[allow(missing_docs)]
110pub struct ExportBox {
111 payload: Arc<dyn ExportableDataPayload + Sync + Send>,
112}
113
114impl PartialEq for ExportBox {
115 fn eq(&self, other: &Self) -> bool {
116 self.payload.eq_dyn(&*other.payload)
117 }
118}
119
120impl Eq for ExportBox {}
121
122impl core::fmt::Debug for ExportBox {
123 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 f.debug_struct("ExportBox")
125 .field("payload", &"<payload>")
126 .finish()
127 }
128}
129
130impl<M> UpcastDataPayload<M> for ExportMarker
131where
132 M: DynamicDataMarker,
133 M::DataStruct: Sync + Send,
134 for<'a> <M::DataStruct as Yokeable<'a>>::Output:
135 Bake + BakeSize + serde::Serialize + MaybeEncodeAsVarULE + PartialEq,
136{
137 fn upcast(other: DataPayload<M>) -> DataPayload<ExportMarker> {
138 DataPayload::from_owned(ExportBox {
139 payload: Arc::new(other),
140 })
141 }
142}
143
144impl DataPayload<ExportMarker> {
145 pub fn serialize<S>(&self, serializer: S) -> Result<(), DataError>
167 where
168 S: serde::Serializer,
169 S::Ok: 'static, {
171 self.get()
172 .payload
173 .serialize_yoke(&mut <dyn erased_serde::Serializer>::erase(serializer))
174 }
175
176 pub fn tokenize(&self, ctx: &CrateEnv) -> TokenStream {
212 self.get().payload.bake_yoke(ctx)
213 }
214
215 pub fn tokenize_encoded_seq(structs: &[&Self], ctx: &CrateEnv) -> Option<TokenStream> {
218 let (first, rest) = structs.split_first()?;
219 first.get().payload.maybe_bake_varule_encoded(rest, ctx)
220 }
221
222 pub fn postcard_size(&self) -> usize {
224 use postcard::ser_flavors::{Flavor, Size};
225 let mut serializer = postcard::Serializer {
226 output: Size::default(),
227 };
228 let _infallible = self
229 .get()
230 .payload
231 .serialize_yoke(&mut <dyn erased_serde::Serializer>::erase(&mut serializer));
232
233 serializer.output.finalize().unwrap_or_default()
234 }
235
236 pub fn baked_size(&self) -> usize {
241 self.get().payload.bake_size()
242 }
243}
244
245impl core::hash::Hash for DataPayload<ExportMarker> {
246 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
247 self.hash_and_postcard_size(state);
248 }
249}
250
251impl DataPayload<ExportMarker> {
252 pub fn hash_and_postcard_size<H: core::hash::Hasher>(&self, state: &mut H) -> usize {
254 use postcard::ser_flavors::Flavor;
255
256 struct HashFlavor<'a, H>(&'a mut H, usize);
257 impl<H: core::hash::Hasher> Flavor for HashFlavor<'_, H> {
258 type Output = usize;
259
260 fn try_push(&mut self, data: u8) -> postcard::Result<()> {
261 self.0.write_u8(data);
262 self.1 += 1;
263 Ok(())
264 }
265
266 fn finalize(self) -> postcard::Result<Self::Output> {
267 Ok(self.1)
268 }
269 }
270
271 let mut serializer = postcard::Serializer {
272 output: HashFlavor(state, 0),
273 };
274
275 let _infallible = self
276 .get()
277 .payload
278 .serialize_yoke(&mut <dyn erased_serde::Serializer>::erase(&mut serializer));
279
280 serializer.output.1
281 }
282}
283
284#[allow(clippy::exhaustive_structs)] #[derive(Debug)]
287pub struct ExportMarker {}
288
289impl DynamicDataMarker for ExportMarker {
290 type DataStruct = ExportBox;
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use crate::hello_world::*;
297
298 #[test]
299 fn test_compare_with_dyn() {
300 let payload1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
301 message: "abc".into(),
302 });
303 let payload2: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
304 message: "abc".into(),
305 });
306 let payload3: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
307 message: "def".into(),
308 });
309
310 assert!(payload1.eq_dyn(&payload2));
311 assert!(payload2.eq_dyn(&payload1));
312
313 assert!(!payload1.eq_dyn(&payload3));
314 assert!(!payload3.eq_dyn(&payload1));
315 }
316
317 #[test]
318 fn test_export_marker_partial_eq() {
319 let payload1: DataPayload<ExportMarker> =
320 UpcastDataPayload::upcast(DataPayload::<HelloWorldV1>::from_owned(HelloWorld {
321 message: "abc".into(),
322 }));
323 let payload2: DataPayload<ExportMarker> =
324 UpcastDataPayload::upcast(DataPayload::<HelloWorldV1>::from_owned(HelloWorld {
325 message: "abc".into(),
326 }));
327 let payload3: DataPayload<ExportMarker> =
328 UpcastDataPayload::upcast(DataPayload::<HelloWorldV1>::from_owned(HelloWorld {
329 message: "def".into(),
330 }));
331
332 assert_eq!(payload1, payload2);
333 assert_eq!(payload2, payload1);
334 assert_ne!(payload1, payload3);
335 assert_ne!(payload3, payload1);
336 }
337}