1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Universally Unique Identifiers or UUIDs.
/// Have to allow unused_imports for `string::ToString`. We have to import it so the `Display` trait
/// can also implement `ToString` automatically for us. Clippy complains its unused but it fails to
/// compile without that import.
#[allow(unused_imports)]
use alloc::string::ToString;

use crate::mesh;
use core::fmt::{Display, Error, Formatter};

type UUIDBytes = [u8; 16];

#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)]
pub struct UUID(pub UUIDBytes);

impl UUID {
    // TODO: Write new UUID functions (versions 1-4)
    #[must_use]
    pub fn from_fields(
        time_low: u32,
        time_mid: u16,
        time_high: u16,
        clock_seq: u16,
        node: u64,
    ) -> UUID {
        let tl = time_low.to_le_bytes();
        let tm = time_mid.to_le_bytes();
        let th = time_high.to_le_bytes();
        let cs = clock_seq.to_le_bytes();
        let nb = node.to_le_bytes();
        // This is a dumb way of building a UUID from byte slices but it should work.
        UUID([
            tl[0], tl[1], tl[2], tl[3], tm[0], tm[1], th[0], th[1], cs[0], cs[1], nb[0], nb[1],
            nb[2], nb[3], nb[4], nb[5],
        ])
    }
    #[must_use]
    pub fn time_low(&self) -> u32 {
        u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]])
    }
    #[must_use]
    pub fn time_mid(&self) -> u16 {
        u16::from_le_bytes([self.0[4], self.0[5]])
    }
    #[must_use]
    pub fn time_high(&self) -> u16 {
        u16::from_le_bytes([self.0[6], self.0[7]])
    }
    #[must_use]
    pub fn clock_seq(&self) -> u16 {
        u16::from_le_bytes([self.0[8], self.0[9]])
    }
    #[must_use]
    pub fn node(&self) -> u64 {
        u64::from_le_bytes([
            self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15], 0, 0,
        ])
    }
    /// Converts a 32-character hex string (`70cf7c9732a345b691494810d2e9cbf4`) to `UUIDBytes`.
    #[must_use]
    pub fn uuid_bytes_from_str(s: &str) -> Option<UUIDBytes> {
        Some(mesh::bytes_str_to_buf(s)?)
    }
}
impl AsRef<[u8]> for UUID {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}
impl AsMut<[u8]> for UUID {
    fn as_mut(&mut self) -> &mut [u8] {
        &mut self.0
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UUIDFields {
    pub time_low: u32,
    pub time_mid: u16,
    pub time_high: u16,
    pub clock_seq: u16,
    pub node: u64,
}

impl Into<UUIDFields> for &UUID {
    #[must_use]
    fn into(self) -> UUIDFields {
        UUIDFields {
            time_low: self.time_low(),
            time_mid: self.time_mid(),
            time_high: self.time_high(),
            clock_seq: self.clock_seq(),
            node: self.node(),
        }
    }
}
impl Into<UUIDFields> for UUID {
    fn into(self) -> UUIDFields {
        (&self).into()
    }
}
impl Into<UUID> for &UUIDFields {
    #[must_use]
    fn into(self) -> UUID {
        UUID::from_fields(
            self.time_low,
            self.time_mid,
            self.time_high,
            self.clock_seq,
            self.node,
        )
    }
}
impl Into<UUID> for UUIDFields {
    #[must_use]
    fn into(self) -> UUID {
        (&self).into()
    }
}
impl Display for UUID {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        write!(
            f,
            "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
            self.time_low(),
            self.time_mid(),
            self.time_high(),
            self.clock_seq(),
            self.node()
        )
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_format() {
        let time_low = 0x123e4567;
        let time_mid = 0xe89b;
        let time_high = 0x12d3;
        let clock_seq = 0xa456;
        let node = 0x426655440000;
        let uuid = UUID::from_fields(time_low, time_mid, time_high, clock_seq, node);

        let fields: UUIDFields = uuid.into();
        assert_eq!(time_low, fields.time_low);
        assert_eq!(time_mid, fields.time_mid);
        assert_eq!(time_high, fields.time_high);
        assert_eq!(clock_seq, fields.clock_seq);
        assert_eq!(node, fields.node);

        assert_eq!(uuid.to_string(), "123e4567-e89b-12d3-a456-426655440000");
    }
}