1use std::fmt;
4use std::fmt::Debug;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::path::PathBuf;
8
9use derive_more::Deref;
10use downcast_rs::DowncastSync;
11use steam_totp::Secret;
12
13use crate::errors::AuthError;
14use crate::MobileAuthFile;
15
16#[derive(Clone)]
28pub struct SteamUser<MaFileState> {
29 pub(crate) username: String,
30 pub(crate) password: String,
31 pub(crate) parental_code: Option<String>,
32 mafile: MaFileState,
33}
34
35pub(crate) trait IsUser: DowncastSync {
36 fn username(&self) -> &str;
37 fn password(&self) -> &str;
38}
39downcast_rs::impl_downcast!(sync IsUser);
40
41impl<T> IsUser for SteamUser<T>
42where
43 T: 'static + Send + Sync,
44{
45 fn username(&self) -> &str {
46 &self.username
47 }
48
49 fn password(&self) -> &str {
50 &self.password
51 }
52}
53
54impl<'a: 'static, T> IsUser for &'a SteamUser<T>
55where
56 T: 'static + Send + Sync,
57{
58 fn username(&self) -> &str {
59 &self.username
60 }
61
62 fn password(&self) -> &str {
63 &self.password
64 }
65}
66
67#[derive(Debug, Clone, Deref)]
69pub struct PresentMaFile(MobileAuthFile);
70
71#[derive(Debug, Copy, Clone)]
73pub struct AbsentMaFile;
74
75impl<T> Display for SteamUser<T> {
76 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
77 write!(f, "Steam User {}", self.username)
78 }
79}
80
81impl<T> Debug for SteamUser<T> {
82 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
83 f.debug_struct("User")
84 .field("username", &self.username)
85 .finish_non_exhaustive()
86 }
87}
88
89impl SteamUser<AbsentMaFile> {
90 #[must_use]
92 pub fn new(username: String, password: String) -> SteamUser<AbsentMaFile> {
93 SteamUser {
94 username,
95 password,
96 parental_code: None,
97 mafile: AbsentMaFile,
98 }
99 }
100}
101impl<T> SteamUser<T> {
102 #[must_use]
104 pub fn username<S>(mut self, username: S) -> Self
105 where
106 S: ToString,
107 {
108 self.username = username.to_string();
109 self
110 }
111
112 #[must_use]
114 pub fn password<S>(mut self, password: S) -> Self
115 where
116 S: ToString,
117 {
118 self.password = password.to_string();
119 self
120 }
121
122 #[must_use]
124 pub fn parental_code<S>(mut self, parental_code: S) -> Self
125 where
126 S: ToString,
127 {
128 self.parental_code = Some(parental_code.to_string());
129 self
130 }
131}
132
133impl SteamUser<AbsentMaFile> {
134 pub fn with_mafile_from_disk<P>(self, path: P) -> Result<SteamUser<PresentMaFile>, AuthError>
136 where
137 P: Into<PathBuf>,
138 {
139 Ok(SteamUser {
140 username: self.username,
141 password: self.password,
142 parental_code: self.parental_code,
143 mafile: PresentMaFile(MobileAuthFile::from_disk(path)?),
144 })
145 }
146
147 #[allow(missing_docs)]
148 #[must_use]
149 pub fn with_mafile(self, ma_file: MobileAuthFile) -> SteamUser<PresentMaFile> {
150 SteamUser {
151 username: self.username,
152 password: self.password,
153 parental_code: self.parental_code,
154 mafile: PresentMaFile(ma_file),
155 }
156 }
157}
158
159impl SteamUser<PresentMaFile> {
160 pub(crate) fn shared_secret(&self) -> Secret {
161 Secret::from_b64(&self.mafile.shared_secret).unwrap()
162 }
163
164 pub(crate) fn identity_secret(&self) -> Secret {
165 Secret::from_b64(&self.mafile.identity_secret).unwrap()
166 }
167
168 pub(crate) fn device_id(&self) -> &str {
169 self.mafile.device_id.as_deref().clone().unwrap()
170 }
171}