1use core::{hash::Hash, str::FromStr};
2use std::{fmt::Debug, marker::PhantomData};
3
4use bytemuck::{Pod, TransparentWrapper, Zeroable};
5use rapira::{Rapira, RapiraError};
6use rend::u64_be;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
9
10use crate::{
11 GetType, IdStr, ScalarTyp, Typ,
12 enc::IdHasher,
13 error::ArmourError,
14 key_part::{KeyPart, SEQ64_BITS},
15 key_type::KeyType,
16 num_ops::g8bits,
17};
18
19type Result<T, E = ArmourError> = core::result::Result<T, E>;
20
21#[derive(IntoBytes, FromBytes, Immutable, KnownLayout, TransparentWrapper)]
25#[cfg_attr(
26 feature = "rkyv",
27 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
28)]
29#[cfg_attr(feature = "bitcode", derive(bitcode::Encode, bitcode::Decode))]
30#[repr(transparent)]
31#[transparent(u64_be)]
32pub struct Id64<T>(pub u64_be, PhantomData<T>);
33
34unsafe impl<T> Zeroable for Id64<T> {}
35unsafe impl<T: 'static> Pod for Id64<T> {}
36
37impl<T> Clone for Id64<T> {
38 fn clone(&self) -> Self {
39 *self
40 }
41}
42
43impl<T> Copy for Id64<T> {}
44
45impl<T> PartialOrd for Id64<T> {
46 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
47 Some(self.cmp(other))
48 }
49}
50
51impl<T> Ord for Id64<T> {
52 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
53 self.0.cmp(&other.0)
54 }
55}
56
57impl<T> PartialEq for Id64<T> {
58 fn eq(&self, other: &Self) -> bool {
59 self.0 == other.0
60 }
61}
62
63impl<T> Eq for Id64<T> {}
64
65impl<T> PartialEq<[u8; 8]> for Id64<T> {
66 fn eq(&self, other: &[u8; 8]) -> bool {
67 let bytes: &[u8; 8] = zerocopy::transmute_ref!(self);
68 bytes == other
69 }
70}
71
72impl<T> PartialEq<Id64<T>> for [u8; 8] {
73 fn eq(&self, other: &Id64<T>) -> bool {
74 let bytes: &[u8; 8] = zerocopy::transmute_ref!(other);
75 bytes == self
76 }
77}
78
79impl<T> PartialEq<Id64<T>> for Option<Id64<T>> {
80 fn eq(&self, other: &Id64<T>) -> bool {
81 match self {
82 Some(id) => id == other,
83 None => false,
84 }
85 }
86}
87
88impl<T> PartialEq<Option<Id64<T>>> for Id64<T> {
89 fn eq(&self, other: &Option<Id64<T>>) -> bool {
90 match other {
91 Some(id) => id == self,
92 None => false,
93 }
94 }
95}
96
97impl<T> AsRef<Self> for Id64<T> {
98 fn as_ref(&self) -> &Self {
99 self
100 }
101}
102
103impl<T> AsRef<[u8; 8]> for Id64<T> {
104 fn as_ref(&self) -> &[u8; 8] {
105 zerocopy::transmute_ref!(self)
106 }
107}
108
109impl<T> Id64<T> {
110 #[inline]
112 pub fn get(self) -> u64 {
113 self.0.to_native()
114 }
115
116 #[inline]
118 pub fn new(id: u64) -> Self {
119 Id64(u64_be::from_native(id), PhantomData)
120 }
121
122 #[inline]
123 pub fn to_le_bytes(self) -> [u8; 8] {
124 self.0.to_native().to_le_bytes()
125 }
126
127 #[inline]
128 pub fn to_be_bytes(self) -> [u8; 8] {
129 zerocopy::transmute!(self)
130 }
131
132 #[inline]
133 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
134 zerocopy::transmute!(bytes)
135 }
136
137 #[inline]
139 pub fn to_bytes(self) -> [u8; 8] {
140 zerocopy::transmute!(self)
141 }
142
143 #[inline]
145 pub fn from_bytes(bytes: [u8; 8]) -> Self {
146 zerocopy::transmute!(bytes)
147 }
148
149 #[inline]
151 pub fn to_u64(self) -> u64 {
152 zerocopy::transmute!(self)
153 }
154
155 #[inline]
157 pub fn from_u64(bytes: u64) -> Self {
158 zerocopy::transmute!(bytes)
159 }
160
161 #[inline]
163 pub fn get_u32(self) -> Option<u32> {
164 let native = self.0.to_native();
165 if native > u32::MAX as u64 {
166 None
167 } else {
168 Some(native as u32)
169 }
170 }
171
172 #[inline]
173 pub fn increment(mut self) -> Self {
174 self.0 += 1;
175 self
176 }
177
178 #[inline]
180 pub fn group_id(&self) -> u32 {
181 let id = self.get();
182 g8bits(id, SEQ64_BITS)
183 }
184}
185
186impl<T: IdHasher> Id64<T> {
187 #[inline]
188 pub fn deser(s: &str) -> Result<Id64<T>> {
189 let v = T::deser(s)?;
190 Ok(zerocopy::transmute!(v))
191 }
192
193 #[inline]
194 pub fn ser(self) -> IdStr {
195 let u: u64 = zerocopy::transmute!(self);
196 T::ser(u)
197 }
198}
199
200impl<T: IdHasher> TryFrom<&str> for Id64<T> {
201 type Error = ArmourError;
202 fn try_from(val: &str) -> Result<Id64<T>> {
203 Self::deser(val)
204 }
205}
206
207impl TryFrom<&str> for Id64<()> {
208 type Error = ArmourError;
209 fn try_from(val: &str) -> Result<Self> {
210 let val = u64::from_str(val)?;
211 Ok(Self(u64_be::from_native(val), PhantomData))
212 }
213}
214
215impl<T: IdHasher> FromStr for Id64<T> {
216 type Err = ArmourError;
217 fn from_str(s: &str) -> Result<Self> {
218 Self::deser(s)
219 }
220}
221
222impl FromStr for Id64<()> {
223 type Err = ArmourError;
224 fn from_str(s: &str) -> Result<Self> {
225 let val = u64::from_str(s)?;
226 Ok(Self(u64_be::from_native(val), PhantomData))
227 }
228}
229
230impl<T> From<Id64<T>> for u64 {
231 #[inline(always)]
232 fn from(id: Id64<T>) -> Self {
233 id.get()
234 }
235}
236
237impl<T: IdHasher> std::fmt::Display for Id64<T> {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 write!(f, "{}", self.ser())
240 }
241}
242
243impl<T> Debug for Id64<T> {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 f.debug_tuple("ID").field(&self.0).finish()
246 }
247}
248
249impl<T> Hash for Id64<T> {
250 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
251 self.0.hash(state);
252 }
253}
254
255#[cfg(feature = "std")]
256impl<T: IdHasher> Serialize for Id64<T> {
257 fn serialize<S: Serializer>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> {
258 let s = self.ser();
259 serializer.serialize_str(&s)
260 }
261}
262
263#[cfg(feature = "std")]
264impl<'de, T: IdHasher> Deserialize<'de> for Id64<T> {
265 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
266 where
267 D: Deserializer<'de>,
268 {
269 use serde::de::Error;
270 let s: &str = Deserialize::deserialize(deserializer)?;
271 let a = Id64::<T>::deser(s).map_err(|_| D::Error::custom("id value error"))?;
272 Ok(a)
273 }
274}
275
276#[cfg(feature = "std")]
277impl Serialize for Id64<()> {
278 fn serialize<S: Serializer>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> {
279 let s = self.get();
280 serializer.serialize_u64(s)
281 }
282}
283
284#[cfg(feature = "std")]
285impl<'de> Deserialize<'de> for Id64<()> {
286 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
287 where
288 D: Deserializer<'de>,
289 {
290 let s: u64 = Deserialize::deserialize(deserializer)?;
291 Ok(Id64(u64_be::from_native(s), PhantomData))
292 }
293}
294
295impl<T: IdHasher> Rapira for Id64<T> {
296 const STATIC_SIZE: Option<usize> = Some(8);
297 const MIN_SIZE: usize = 8;
298
299 #[inline]
300 fn size(&self) -> usize {
301 8
302 }
303
304 #[inline]
305 fn check_bytes(slice: &mut &[u8]) -> core::result::Result<(), rapira::RapiraError>
306 where
307 Self: Sized,
308 {
309 let bytes: &[u8] = slice.get(..8).ok_or(RapiraError::SliceLen)?;
310
311 if bytes == [0u8; 8] {
312 return Err(RapiraError::NonZero);
313 }
314
315 *slice = unsafe { slice.get_unchecked(8..) };
316 Ok(())
317 }
318
319 #[inline]
320 fn from_slice(slice: &mut &[u8]) -> core::result::Result<Self, rapira::RapiraError>
321 where
322 Self: Sized,
323 {
324 let bytes = <[u8; 8]>::from_slice(slice)?;
325 let id = Self::from_bytes(bytes);
326 Ok(id)
327 }
328
329 #[inline]
330 fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
331 let bytes: &[u8; 8] = zerocopy::transmute_ref!(self);
332 bytes.convert_to_bytes(slice, cursor);
333 }
334
335 #[inline]
336 fn convert_to_bytes_ctx(
337 &self,
338 slice: &mut [u8],
339 cursor: &mut usize,
340 flags: rapira::RapiraFlags,
341 ) {
342 if flags.has(crate::ID_ENC_FLAG) {
343 let u: u64 = zerocopy::transmute!(*self);
344 let id = T::encrypt(u);
345 id.convert_to_bytes(slice, cursor);
346 } else {
347 self.convert_to_bytes(slice, cursor)
348 }
349 }
350
351 #[inline]
352 fn from_slice_ctx(slice: &mut &[u8], flags: rapira::RapiraFlags) -> rapira::Result<Self>
353 where
354 Self: Sized,
355 {
356 if flags.has(crate::ID_ENC_FLAG) {
357 let id = u64::from_slice(slice)?;
358 let id = T::decrypt(id);
359 Ok(zerocopy::transmute!(id))
360 } else {
361 Self::from_slice(slice)
362 }
363 }
364}
365
366impl Rapira for Id64<()> {
367 const STATIC_SIZE: Option<usize> = Some(8);
368 const MIN_SIZE: usize = 8;
369
370 #[inline]
371 fn size(&self) -> usize {
372 8
373 }
374
375 #[inline]
376 fn check_bytes(slice: &mut &[u8]) -> core::result::Result<(), rapira::RapiraError>
377 where
378 Self: Sized,
379 {
380 let bytes: &[u8] = slice.get(..8).ok_or(RapiraError::SliceLen)?;
381
382 if bytes == [0u8; 8] {
383 return Err(RapiraError::NonZero);
384 }
385
386 *slice = unsafe { slice.get_unchecked(8..) };
387 Ok(())
388 }
389
390 #[inline]
391 fn from_slice(slice: &mut &[u8]) -> core::result::Result<Self, rapira::RapiraError>
392 where
393 Self: Sized,
394 {
395 let bytes = <[u8; 8]>::from_slice(slice)?;
396 let id = Self::from_bytes(bytes);
397 Ok(id)
398 }
399
400 #[inline]
401 fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
402 let bytes: &[u8; 8] = zerocopy::transmute_ref!(self);
403 bytes.convert_to_bytes(slice, cursor);
404 }
405}
406
407impl<T> GetType for Id64<T> {
408 const TYPE: Typ = Typ::Scalar(ScalarTyp::Id64);
409}
410
411impl<H> KeyPart for Id64<H> {
412 const TY: KeyType = KeyType::U64;
413 const PREFIX_BITS: u32 = SEQ64_BITS;
414}
415
416#[cfg(feature = "ts-rs")]
417impl<T> ts_rs::TS for Id64<T> {
418 type WithoutGenerics = Id64<()>;
419 type OptionInnerType = Self;
420 fn name(_: &ts_rs::Config) -> String {
421 "Id64".to_owned()
422 }
423 fn decl_concrete(c: &ts_rs::Config) -> String {
424 format!("type {} = {};", Self::name(c), Self::inline(c))
425 }
426 fn decl(c: &ts_rs::Config) -> String {
427 let inline = <Id64<()> as ::ts_rs::TS>::inline(c);
428 format!("type {} = {};", Self::name(c), inline)
429 }
430 fn inline(_: &ts_rs::Config) -> String {
431 "string".to_owned()
432 }
433 fn inline_flattened(c: &ts_rs::Config) -> String {
434 panic!("{} cannot be flattened", Self::name(c))
435 }
436 fn output_path() -> Option<std::path::PathBuf> {
437 Some(std::path::PathBuf::from("id64.ts"))
438 }
439}
440
441#[cfg(feature = "facet")]
442unsafe impl<'facet, T: 'static> facet::Facet<'facet> for Id64<T> {
443 const SHAPE: &'static facet::Shape = &const {
444 const VTABLE: facet::VTableDirect = facet::vtable_direct!(Id64<()> =>
445 Debug,
446 Hash,
447 PartialEq,
448 PartialOrd,
449 Ord,
450 );
451
452 facet::ShapeBuilder::for_sized::<Id64<T>>("Id64")
453 .ty(facet::Type::User(facet::UserType::Struct(facet::StructType {
454 repr: facet::Repr::transparent(),
455 kind: facet::StructKind::TupleStruct,
456 fields: &const {
457 [facet::FieldBuilder::new("0", facet::shape_of::<u64>, 0).build()]
458 },
459 })))
460 .inner(<u64 as facet::Facet>::SHAPE)
461 .def(facet::Def::Scalar)
462 .vtable_direct(&VTABLE)
463 .eq()
464 .copy()
465 .send()
466 .sync()
467 .build()
468 };
469}
470
471#[cfg(feature = "fake")]
472impl<H, T> fake::Dummy<T> for Id64<H> {
473 fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, _: &mut R) -> Self {
474 use fake::Fake;
475
476 let u = (0..100_000_000).fake::<u64>();
477 Self::new(u)
478 }
479}
480
481#[derive(IntoBytes, FromBytes, Immutable, KnownLayout, TransparentWrapper)]
499#[cfg_attr(
500 feature = "rkyv",
501 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
502)]
503#[cfg_attr(feature = "bitcode", derive(bitcode::Encode, bitcode::Decode))]
504#[repr(transparent)]
505#[transparent(u64_be)]
506pub struct OptId64<T>(u64_be, PhantomData<T>);
507
508unsafe impl<T> Zeroable for OptId64<T> {}
509unsafe impl<T: 'static> Pod for OptId64<T> {}
510
511impl<T> Clone for OptId64<T> {
512 fn clone(&self) -> Self {
513 *self
514 }
515}
516
517impl<T> Copy for OptId64<T> {}
518
519impl<T> OptId64<T> {
520 pub const NONE: Self = unsafe { core::mem::zeroed() };
522
523 #[inline]
524 pub fn some(id: Id64<T>) -> Self {
525 Self(id.0, PhantomData)
526 }
527
528 #[inline]
529 pub fn get(self) -> Option<Id64<T>> {
530 if self.0.to_native() == 0 {
531 None
532 } else {
533 Some(Id64(self.0, PhantomData))
534 }
535 }
536
537 #[inline]
538 pub fn is_some(self) -> bool {
539 self.0.to_native() != 0
540 }
541
542 #[inline]
543 pub fn is_none(self) -> bool {
544 self.0.to_native() == 0
545 }
546
547 #[inline]
549 pub fn get_raw(self) -> u64 {
550 self.0.to_native()
551 }
552
553 #[inline]
555 pub fn to_bytes(self) -> [u8; 8] {
556 zerocopy::transmute!(self)
557 }
558
559 #[inline]
561 pub fn from_bytes(bytes: [u8; 8]) -> Self {
562 zerocopy::transmute!(bytes)
563 }
564
565 #[inline]
566 pub fn to_be_bytes(self) -> [u8; 8] {
567 zerocopy::transmute!(self)
568 }
569
570 #[inline]
571 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
572 zerocopy::transmute!(bytes)
573 }
574}
575
576impl<T> Default for OptId64<T> {
577 fn default() -> Self {
578 Self::NONE
579 }
580}
581
582impl<T> From<Id64<T>> for OptId64<T> {
583 fn from(id: Id64<T>) -> Self {
584 Self::some(id)
585 }
586}
587
588impl<T> From<Option<Id64<T>>> for OptId64<T> {
589 fn from(opt: Option<Id64<T>>) -> Self {
590 match opt {
591 Some(id) => Self::some(id),
592 None => Self::NONE,
593 }
594 }
595}
596
597impl<T> From<OptId64<T>> for Option<Id64<T>> {
598 fn from(opt: OptId64<T>) -> Self {
599 opt.get()
600 }
601}
602
603impl<T> PartialEq for OptId64<T> {
604 fn eq(&self, other: &Self) -> bool {
605 self.0 == other.0
606 }
607}
608
609impl<T> Eq for OptId64<T> {}
610
611impl<T> PartialOrd for OptId64<T> {
612 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
613 Some(self.cmp(other))
614 }
615}
616
617impl<T> Ord for OptId64<T> {
618 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
619 self.0.cmp(&other.0)
620 }
621}
622
623impl<T> Hash for OptId64<T> {
624 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
625 self.0.hash(state);
626 }
627}
628
629impl<T> PartialEq<Id64<T>> for OptId64<T> {
630 fn eq(&self, other: &Id64<T>) -> bool {
631 self.0 == other.0
632 }
633}
634
635impl<T> PartialEq<OptId64<T>> for Id64<T> {
636 fn eq(&self, other: &OptId64<T>) -> bool {
637 self.0 == other.0
638 }
639}
640
641impl<T> AsRef<[u8; 8]> for OptId64<T> {
642 fn as_ref(&self) -> &[u8; 8] {
643 zerocopy::transmute_ref!(self)
644 }
645}
646
647impl<T> Debug for OptId64<T> {
648 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
649 match self.0.to_native() {
650 0 => write!(f, "OptID(None)"),
651 _ => f.debug_tuple("OptID").field(&self.0).finish(),
652 }
653 }
654}
655
656impl<T: IdHasher> Rapira for OptId64<T> {
657 const STATIC_SIZE: Option<usize> = Some(8);
658 const MIN_SIZE: usize = 8;
659
660 #[inline]
661 fn size(&self) -> usize {
662 8
663 }
664
665 #[inline]
666 fn check_bytes(slice: &mut &[u8]) -> core::result::Result<(), rapira::RapiraError>
667 where
668 Self: Sized,
669 {
670 if slice.len() < 8 {
671 return Err(RapiraError::SliceLen);
672 }
673 *slice = unsafe { slice.get_unchecked(8..) };
674 Ok(())
675 }
676
677 #[inline]
678 fn from_slice(slice: &mut &[u8]) -> core::result::Result<Self, rapira::RapiraError>
679 where
680 Self: Sized,
681 {
682 let bytes = <[u8; 8]>::from_slice(slice)?;
683 Ok(Self::from_bytes(bytes))
684 }
685
686 #[inline]
687 fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
688 let bytes: &[u8; 8] = zerocopy::transmute_ref!(self);
689 bytes.convert_to_bytes(slice, cursor);
690 }
691
692 #[inline]
693 fn convert_to_bytes_ctx(
694 &self,
695 slice: &mut [u8],
696 cursor: &mut usize,
697 flags: rapira::RapiraFlags,
698 ) {
699 if flags.has(crate::ID_ENC_FLAG) && self.is_some() {
700 let u: u64 = zerocopy::transmute!(*self);
701 let id = T::encrypt(u);
702 id.convert_to_bytes(slice, cursor);
703 } else {
704 self.convert_to_bytes(slice, cursor)
705 }
706 }
707
708 #[inline]
709 fn from_slice_ctx(
710 slice: &mut &[u8],
711 flags: rapira::RapiraFlags,
712 ) -> core::result::Result<Self, rapira::RapiraError>
713 where
714 Self: Sized,
715 {
716 if flags.has(crate::ID_ENC_FLAG) {
717 let bytes = <[u8; 8]>::from_slice(slice)?;
718 if bytes == [0u8; 8] {
719 Ok(Self::NONE)
720 } else {
721 let id = u64::from_le_bytes(bytes);
722 let id = T::decrypt(id);
723 Ok(zerocopy::transmute!(id))
724 }
725 } else {
726 Self::from_slice(slice)
727 }
728 }
729}
730
731impl Rapira for OptId64<()> {
732 const STATIC_SIZE: Option<usize> = Some(8);
733 const MIN_SIZE: usize = 8;
734
735 #[inline]
736 fn size(&self) -> usize {
737 8
738 }
739
740 #[inline]
741 fn check_bytes(slice: &mut &[u8]) -> core::result::Result<(), rapira::RapiraError>
742 where
743 Self: Sized,
744 {
745 if slice.len() < 8 {
746 return Err(RapiraError::SliceLen);
747 }
748 *slice = unsafe { slice.get_unchecked(8..) };
749 Ok(())
750 }
751
752 #[inline]
753 fn from_slice(slice: &mut &[u8]) -> core::result::Result<Self, rapira::RapiraError>
754 where
755 Self: Sized,
756 {
757 let bytes = <[u8; 8]>::from_slice(slice)?;
758 Ok(Self::from_bytes(bytes))
759 }
760
761 #[inline]
762 fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
763 let bytes: &[u8; 8] = zerocopy::transmute_ref!(self);
764 bytes.convert_to_bytes(slice, cursor);
765 }
766}
767
768impl<T> GetType for OptId64<T> {
769 const TYPE: Typ = Typ::Scalar(ScalarTyp::Id64);
770}
771
772#[cfg(feature = "std")]
773impl<T: IdHasher> Serialize for OptId64<T> {
774 fn serialize<S: Serializer>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> {
775 match self.get() {
776 Some(id) => serializer.serialize_str(&id.ser()),
777 None => serializer.serialize_none(),
778 }
779 }
780}
781
782#[cfg(feature = "std")]
783impl<'de, T: IdHasher> Deserialize<'de> for OptId64<T> {
784 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
785 where
786 D: Deserializer<'de>,
787 {
788 use serde::de::Error;
789 let s: Option<&str> = Deserialize::deserialize(deserializer)?;
790 match s {
791 Some(s) => {
792 let id = Id64::<T>::deser(s).map_err(|_| D::Error::custom("id value error"))?;
793 Ok(Self::some(id))
794 }
795 None => Ok(Self::NONE),
796 }
797 }
798}
799
800#[cfg(feature = "std")]
801impl Serialize for OptId64<()> {
802 fn serialize<S: Serializer>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> {
803 match self.get() {
804 Some(id) => serializer.serialize_some(&id.get()),
805 None => serializer.serialize_none(),
806 }
807 }
808}
809
810#[cfg(feature = "std")]
811impl<'de> Deserialize<'de> for OptId64<()> {
812 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
813 where
814 D: Deserializer<'de>,
815 {
816 let s: Option<u64> = Deserialize::deserialize(deserializer)?;
817 match s {
818 Some(v) => Ok(Self::some(Id64::new(v))),
819 None => Ok(Self::NONE),
820 }
821 }
822}
823
824#[cfg(feature = "ts-rs")]
825impl<T> ts_rs::TS for OptId64<T> {
826 type WithoutGenerics = OptId64<()>;
827 type OptionInnerType = Self;
828 fn name(_: &ts_rs::Config) -> String {
829 "OptId64".to_owned()
830 }
831 fn decl_concrete(c: &ts_rs::Config) -> String {
832 format!("type OptId64 = {};", Self::inline(c))
833 }
834 fn decl(c: &ts_rs::Config) -> String {
835 let inline = <OptId64<()> as ::ts_rs::TS>::inline(c);
836 format!("type OptId64 = {inline};")
837 }
838 fn inline(_: &ts_rs::Config) -> String {
839 "string | null".to_owned()
840 }
841 fn inline_flattened(c: &ts_rs::Config) -> String {
842 panic!("{} cannot be flattened", Self::name(c))
843 }
844 fn output_path() -> Option<std::path::PathBuf> {
845 Some(std::path::PathBuf::from("opt_id64.ts"))
846 }
847}
848
849#[cfg(feature = "facet")]
850unsafe impl<'facet, T: 'static> facet::Facet<'facet> for OptId64<T> {
851 const SHAPE: &'static facet::Shape = &const {
852 const VTABLE: facet::VTableDirect = facet::vtable_direct!(OptId64<()> =>
853 Debug,
854 Hash,
855 PartialEq,
856 PartialOrd,
857 Ord,
858 );
859
860 facet::ShapeBuilder::for_sized::<OptId64<T>>("OptId64")
861 .ty(facet::Type::User(facet::UserType::Struct(facet::StructType {
862 repr: facet::Repr::transparent(),
863 kind: facet::StructKind::TupleStruct,
864 fields: &const {
865 [facet::FieldBuilder::new("0", facet::shape_of::<u64>, 0).build()]
866 },
867 })))
868 .inner(<u64 as facet::Facet>::SHAPE)
869 .def(facet::Def::Scalar)
870 .vtable_direct(&VTABLE)
871 .eq()
872 .copy()
873 .send()
874 .sync()
875 .build()
876 };
877}
878
879#[cfg(feature = "fake")]
880impl<H, T> fake::Dummy<T> for OptId64<H> {
881 fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &T, _: &mut R) -> Self {
882 use fake::Fake;
883 let u = (0..100_000_000).fake::<u64>();
884 Self::some(Id64::new(u))
885 }
886}
887
888#[cfg(test)]
889mod tests {
890 use super::*;
891
892 #[test]
893 fn test_increment_basic() {
894 let id = Id64::<()>::new(42);
895 let incremented = id.increment();
896 assert_eq!(incremented.get(), 43);
897 }
898
899 #[test]
900 fn test_increment_multiple() {
901 let id = Id64::<()>::new(100);
902 let id = id.increment();
903 assert_eq!(id.get(), 101);
904 let id = id.increment();
905 assert_eq!(id.get(), 102);
906 let id = id.increment();
907 assert_eq!(id.get(), 103);
908 }
909
910 #[test]
911 fn test_increment_zero() {
912 let id = Id64::<()>::new(0);
913 let incremented = id.increment();
914 assert_eq!(incremented.get(), 1);
915 }
916
917 #[test]
918 fn test_increment_large_value() {
919 let id = Id64::<()>::new(u64::MAX - 1);
920 let incremented = id.increment();
921 assert_eq!(incremented.get(), u64::MAX);
922 }
923
924 #[test]
925 #[cfg(debug_assertions)]
926 #[should_panic(expected = "attempt to add with overflow")]
927 fn test_increment_overflow_debug() {
928 let id = Id64::<()>::new(u64::MAX);
929 let _ = id.increment(); }
931
932 #[test]
933 #[cfg(not(debug_assertions))]
934 fn test_increment_overflow_release() {
935 let id = Id64::<()>::new(u64::MAX);
936 let incremented = id.increment();
937 assert_eq!(incremented.get(), 0);
939 }
940
941 #[test]
942 fn test_increment_endianness() {
943 let id = Id64::<()>::new(255); let incremented = id.increment();
946 assert_eq!(incremented.get(), 256); let bytes = incremented.to_be_bytes();
950 assert_eq!(bytes, [0, 0, 0, 0, 0, 0, 1, 0]);
951 }
952
953 #[test]
956 fn test_opt_id64_none() {
957 let opt = OptId64::<()>::NONE;
958 assert!(opt.is_none());
959 assert!(!opt.is_some());
960 assert_eq!(opt.get(), None);
961 assert_eq!(opt.to_bytes(), [0u8; 8]);
962 }
963
964 #[test]
965 fn test_opt_id64_some() {
966 let id = Id64::<()>::new(42);
967 let opt = OptId64::some(id);
968 assert!(opt.is_some());
969 assert!(!opt.is_none());
970 assert_eq!(opt.get(), Some(id));
971 }
972
973 #[test]
974 fn test_opt_id64_conversions() {
975 let id = Id64::<()>::new(42);
976
977 let opt: OptId64<()> = id.into();
978 assert_eq!(opt.get(), Some(id));
979
980 let opt: OptId64<()> = None.into();
981 assert!(opt.is_none());
982
983 let opt: OptId64<()> = Some(id).into();
984 let back: Option<Id64<()>> = opt.into();
985 assert_eq!(back, Some(id));
986 }
987
988 #[test]
989 fn test_opt_id64_size() {
990 assert_eq!(size_of::<OptId64<()>>(), 8);
991 assert_eq!(size_of::<OptId64<()>>(), size_of::<Id64<()>>());
992 }
993
994 #[test]
995 fn test_opt_id64_eq_with_id64() {
996 let id = Id64::<()>::new(42);
997 let opt = OptId64::some(id);
998 assert_eq!(opt, id);
999 assert_eq!(id, opt);
1000 }
1001
1002 #[test]
1003 fn test_opt_id64_default() {
1004 let opt = OptId64::<()>::default();
1005 assert!(opt.is_none());
1006 }
1007
1008 #[test]
1009 fn test_id64_key_part_ty() {
1010 assert_eq!(<Id64<()> as KeyPart>::TY, KeyType::U64);
1011 }
1012
1013 use crate::enc::{Cipher, IdHasher};
1016
1017 #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
1018 pub struct Hasher;
1019
1020 impl IdHasher for Hasher {
1021 const HASHER: Cipher = Cipher::new(
1022 "_mKbKGF2IrkGvIJvl97HuCgWjgt6QRZ7Ye8DHBQ2anvyi18BdMz8uN6Ej3YJApooY6qDu0obqq4",
1023 );
1024 }
1025
1026 #[test]
1027 fn test_id64_ctx_roundtrip() {
1028 let id = Id64::<Hasher>::new(123456789);
1029 let flags = rapira::RapiraFlags::new(crate::ID_ENC_FLAG);
1030
1031 let encrypted = rapira::serialize_ctx(&id, flags);
1032 let plain = rapira::serialize(&id);
1033 assert_ne!(encrypted, plain);
1034
1035 let decoded: Id64<Hasher> = rapira::deserialize_ctx(&encrypted, flags).unwrap();
1036 assert_eq!(decoded, id);
1037 }
1038
1039 #[test]
1040 fn test_id64_ctx_no_flag_same_as_plain() {
1041 let id = Id64::<Hasher>::new(123456789);
1042 let plain = rapira::serialize(&id);
1043 let ctx_none = rapira::serialize_ctx(&id, rapira::RapiraFlags::NONE);
1044 assert_eq!(plain, ctx_none);
1045 }
1046
1047 #[test]
1048 fn test_opt_id64_ctx_none_roundtrip() {
1049 let opt = OptId64::<Hasher>::NONE;
1050 let flags = rapira::RapiraFlags::new(crate::ID_ENC_FLAG);
1051 let bytes = rapira::serialize_ctx(&opt, flags);
1052 assert_eq!(bytes, [0u8; 8]);
1053 let decoded: OptId64<Hasher> = rapira::deserialize_ctx(&bytes, flags).unwrap();
1054 assert!(decoded.is_none());
1055 }
1056
1057 #[test]
1058 fn test_opt_id64_ctx_some_roundtrip() {
1059 let id = Id64::<Hasher>::new(123456789);
1060 let opt = OptId64::some(id);
1061 let flags = rapira::RapiraFlags::new(crate::ID_ENC_FLAG);
1062
1063 let encrypted = rapira::serialize_ctx(&opt, flags);
1064 let plain = rapira::serialize(&opt);
1065 assert_ne!(encrypted, plain);
1066
1067 let decoded: OptId64<Hasher> = rapira::deserialize_ctx(&encrypted, flags).unwrap();
1068 assert_eq!(decoded.get(), Some(id));
1069 }
1070}