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