scream_id/
lib.rs

1const ACCOUNT_ID_MASK: u64 = 0xFFFFFFFF;
2const ACCOUNT_INSTANCE_MASK: u64 = 0x000FFFFF;
3
4#[derive(PartialEq, Debug)]
5pub enum Universe {
6    Invalid = 0,
7    Public = 1,
8    Beta = 2,
9    Internal = 3,
10    Dev = 4,
11}
12
13#[derive(PartialEq, Debug)]
14pub enum Type {
15    Invalid = 0,
16    Individual = 1,
17    Multiseat = 2,
18    GameServer = 3,
19    AnonGameServer = 4,
20    Pending = 5,
21    ContentServer = 6,
22    Clan = 7,
23    Chat = 8,
24    P2PSuperSeeder = 9,
25    AnonUser = 10,
26}
27
28#[derive(PartialEq, Debug)]
29pub enum Instance {
30    All = 0,
31    Desktop = 1,
32    Console = 2,
33    Web = 4,
34}
35
36#[derive(PartialEq, Debug)]
37pub struct SteamID {
38    universe: Universe,
39    type_: Type,
40    instance: Instance,
41    account_id: u32,
42}
43
44impl Universe {
45    fn from_u32(value: u32) -> Option<Universe> {
46        match value {
47            0 => Some(Universe::Invalid),
48            1 => Some(Universe::Public),
49            2 => Some(Universe::Beta),
50            3 => Some(Universe::Internal),
51            4 => Some(Universe::Dev),
52            _ => None,
53        }
54    }
55}
56
57impl Type {
58    fn from_u32(value: u32) -> Option<Type> {
59        match value {
60            0 => Some(Type::Invalid),
61            1 => Some(Type::Individual),
62            2 => Some(Type::Multiseat),
63            3 => Some(Type::GameServer),
64            4 => Some(Type::AnonGameServer),
65            5 => Some(Type::Pending),
66            6 => Some(Type::ContentServer),
67            7 => Some(Type::Clan),
68            8 => Some(Type::Chat),
69            9 => Some(Type::P2PSuperSeeder),
70            10 => Some(Type::AnonUser),
71            _ => None,
72        }
73    }
74}
75
76impl Instance {
77    fn from_u32(value: u32) -> Option<Instance> {
78        match value {
79            0 => Some(Instance::All),
80            1 => Some(Instance::Desktop),
81            2 => Some(Instance::Console),
82            4 => Some(Instance::Web),
83            _ => None,
84        }
85    }
86}
87
88impl SteamID {
89    /// Attempt to parse a SteamID from a string.
90    /// Returns None if the input is not a valid SteamID.
91    /// You can pass it any kind of SteamID. (EXCEPT Steam3 IDS TODO!)
92    ///
93    /// # Examples:
94    ///
95    /// ```
96    /// let steamid = scream_id::SteamID::new("23");
97    /// assert_eq!(steamid, None);
98    /// ```
99    pub fn new(input: &str) -> Option<Self> {
100        let mut id = Self {
101            universe: Universe::Invalid,
102            type_: Type::Invalid,
103            instance: Instance::All,
104            account_id: 0,
105        };
106
107        if let Some(id64) = SteamID::validate_steam64(input) {
108            id.universe = Universe::from_u32((id64 >> 56) as u32).unwrap_or(Universe::Invalid);
109            id.type_ = Type::from_u32(((id64 >> 52) & 0xF) as u32).unwrap_or(Type::Invalid);
110            id.instance = Instance::from_u32(((id64 >> 32) & ACCOUNT_INSTANCE_MASK) as u32)
111                .unwrap_or(Instance::All);
112            id.account_id = (id64 & ACCOUNT_ID_MASK) as u32;
113        } else if let Some(id2) = SteamID::validate_steam2(input) {
114            let mut parts = id2.split(':');
115
116            parts.next();
117            let universe = parts.next().unwrap();
118            let account_id = parts.next().unwrap();
119
120            id.type_ = Type::Individual;
121            id.instance = Instance::Desktop;
122            id.account_id = account_id.parse::<u32>().unwrap();
123            id.universe = match universe.parse::<u32>().unwrap() {
124                0 => Universe::Public, // If 0 it should be public?
125                _ => {
126                    Universe::from_u32(universe.parse::<u32>().unwrap()).unwrap_or(Universe::Public)
127                }
128            }
129        } else {
130            return None;
131        }
132
133        Some(id)
134    }
135
136    /// Tries to render the SteamID as a string.
137    ///
138    /// # Examples:
139    /// ```
140    /// let steamid = scream_id::SteamID::new("76561198403256399");
141    ///
142    /// assert_eq!(steamid.unwrap().render_as_steam2(), Some(String::from("STEAM_0:1:221495335")));
143    /// ```
144    pub fn render_as_steam2<'a>(self) -> Option<String> {
145        if self.type_ != Type::Individual {
146            return None;
147        }
148
149        let mut universe = self.universe as u32;
150
151        if universe == 1 {
152            universe = 0;
153        }
154
155        Some(format!(
156            "STEAM_{}:{}:{}",
157            universe,
158            self.instance as u32,
159            ((self.account_id / 2) as f64).floor()
160        ))
161    }
162
163    /*
164    pub fn validate_3(input: &str) -> Option<&str> {
165        todo!()
166    }
167     */
168
169    /// Validates a Steam2 id and returns it if it is valid.
170    ///
171    /// # Examples:
172    ///
173    /// ```
174    /// let id = scream_id::SteamID::validate_steam2("STEAM_0:1:221495335").unwrap();
175    ///
176    /// assert_eq!(id,"STEAM_0:1:221495335");
177    /// ```
178    pub fn validate_steam2(input: &str) -> Option<&str> {
179        // EG: STEAM_0:0:23071901
180
181        let mut parts = input.split(':');
182
183        if parts.clone().count() != 3 {
184            return None;
185        }
186
187        let universe = parts.next().unwrap();
188
189        if universe != "STEAM_0" && universe != "STEAM_1" {
190            return None;
191        }
192
193        Some(input)
194    }
195
196    /// Validates a SteamID64 and returns it if it is valid.
197    ///
198    /// # Examples:
199    ///
200    /// ```
201    /// let id = scream_id::SteamID::validate_steam64("76561198403256399").unwrap();
202    ///
203    /// assert_eq!(id,76561198403256399);
204    /// ```
205    pub fn validate_steam64(input: &str) -> Option<u64> {
206        if input.len() == 17 {
207            if let Ok(id) = u64::from_str_radix(input, 10) {
208                if (id & ACCOUNT_ID_MASK) != 0 {
209                    return Some(id);
210                }
211            }
212        }
213
214        None
215    }
216}