1use std::fmt;
2use std::sync::atomic::Ordering;
3
4use serde::{de, ser};
5use crate::profile::{Profile, ProfileTag};
6#[derive(Copy, Clone)]
17pub struct Tag(u64);
18
19#[cfg(any(target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32"))]
20static COUNTER: atomic::Atomic<u64> = atomic::Atomic::new(1);
21
22#[cfg(not(any(target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32")))]
23static COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
24
25
26impl Tag {
27 #[allow(non_upper_case_globals)]
31 pub const Default: Tag = Tag(0);
32
33 const PROFILE_TAG_SHIFT: u64 = 62;
34 const PROFILE_TAG_MASK: u64 = 0b11 << Self::PROFILE_TAG_SHIFT;
35
36 const METADATA_ID_SHIFT: u64 = 0;
37 const METADATA_ID_MASK: u64 = (!Self::PROFILE_TAG_MASK) << Self::METADATA_ID_SHIFT;
38
39 const fn new(metadata_id: u64, profile_tag: ProfileTag) -> Tag {
40 let bits = ((metadata_id << Self::METADATA_ID_SHIFT) & Self::METADATA_ID_MASK)
41 | ((profile_tag as u64) << Self::PROFILE_TAG_SHIFT) & Self::PROFILE_TAG_MASK;
42
43 Tag(bits)
44 }
45
46 pub(crate) fn next() -> Tag {
48 let id = COUNTER.fetch_add(1, Ordering::AcqRel);
49 if id > Self::METADATA_ID_MASK {
50 panic!("figment: out of unique tag IDs");
51 }
52
53 Tag::new(id, ProfileTag::Default)
54 }
55
56 pub(crate) fn metadata_id(self) -> u64 {
57 (self.0 & Self::METADATA_ID_MASK) >> Self::METADATA_ID_SHIFT
58 }
59
60 pub(crate) fn profile_tag(self) -> ProfileTag {
61 let bits = (self.0 & Self::PROFILE_TAG_MASK) >> Self::PROFILE_TAG_SHIFT;
62 (bits as u8).into()
63 }
64
65 pub(crate) fn for_profile(self, profile: &crate::Profile) -> Self {
66 Tag::new(self.metadata_id(), profile.into())
67 }
68
69 pub const fn is_default(self) -> bool {
79 self.0 == Tag::Default.0
80 }
81
82 pub fn profile(self) -> Option<Profile> {
94 self.profile_tag().into()
95 }
96}
97
98impl Default for Tag {
99 fn default() -> Self {
100 Tag::Default
101 }
102}
103
104impl PartialEq for Tag {
105 fn eq(&self, other: &Self) -> bool {
106 self.metadata_id() == other.metadata_id()
107 }
108}
109
110impl Eq for Tag { }
111
112impl PartialOrd for Tag {
113 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
114 self.metadata_id().partial_cmp(&other.metadata_id())
115 }
116}
117
118impl Ord for Tag {
119 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120 self.metadata_id().cmp(&other.metadata_id())
121 }
122}
123
124impl std::hash::Hash for Tag {
125 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
126 state.write_u64(self.metadata_id())
127 }
128}
129
130impl From<Tag> for crate::value::Value {
131 fn from(tag: Tag) -> Self {
132 crate::value::Value::from(tag.0)
133 }
134}
135
136impl<'de> de::Deserialize<'de> for Tag {
137 fn deserialize<D>(deserializer: D) -> Result<Tag, D::Error>
138 where D: de::Deserializer<'de>
139 {
140 struct Visitor;
141
142 impl<'de> de::Visitor<'de> for Visitor {
143 type Value = Tag;
144
145 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
146 f.write_str("a 64-bit metadata id integer")
147 }
148
149 fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
150 Ok(Tag(v))
151 }
152 }
153
154 deserializer.deserialize_any(Visitor)
155 }
156}
157
158impl ser::Serialize for Tag {
159 fn serialize<S: ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
160 ser.serialize_u64(self.0)
161 }
162}
163
164impl fmt::Debug for Tag {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 t if t.is_default() => write!(f, "Tag::Default"),
168 _ => write!(f, "Tag({:?}, {})", self.profile_tag(), self.metadata_id())
169 }
170 }
171}