1use std::ops::Deref;
2
3use crate::log;
4use crate::DateTime;
5
6#[derive(Debug)]
7pub struct World {
8 pub id: String,
9 pub name: String,
10}
11
12#[derive(Debug, PartialEq)]
13pub enum InstanceType {
14 Public,
15 FriendPlus,
16 Friends,
17 InvitePlus,
18 Invite,
19
20 Unknown,
21}
22
23#[derive(Debug)]
24pub struct Instance {
25 pub world: World,
26 pub id: u32,
27 pub owner: Option<String>,
28 pub typ: InstanceType,
29 pub nonce: Option<String>,
30}
31
32#[derive(Debug)]
33pub struct InstanceLog {
34 pub instance: Instance,
35 pub enter: Option<DateTime>,
36 pub join: Option<DateTime>,
37 pub join_or_create: Option<DateTime>,
38 pub joined: Option<DateTime>,
39 pub left: Option<DateTime>,
40}
41
42#[derive(Debug)]
43pub struct InstanceLogList(Vec<InstanceLog>);
44
45#[derive(Debug)]
46pub enum InstanceParseError {
47 IdIsNotNumber(String),
48 InvalidElement(String),
49 UnknownElement(String),
50 Invalid(String),
51 InvalidReqInvite(InstanceType),
52}
53
54pub fn parse_instance(s: &str) -> Result<Instance, InstanceParseError> {
57 let s = s
58 .strip_prefix("wrld_")
59 .ok_or(InstanceParseError::Invalid(s.to_string()))?;
60
61 let mut elem: Vec<&str> = s.split('~').collect();
63 elem.retain(|w| !w.is_empty());
64
65 let mut typ = InstanceType::Unknown;
66 if elem.len() == 1 {
67 typ = InstanceType::Public;
69 }
70 let mut it = elem.iter();
71
72 let world = it
74 .next()
75 .ok_or(InstanceParseError::Invalid(s.to_string()))?;
76 let w: Vec<&str> = world.split(':').collect();
77 if w.len() != 2 {
78 Err(InstanceParseError::InvalidElement(world.to_string()))?
79 }
80 let id = w[1].parse().unwrap();
81
82 let world = World {
83 id: w[0].to_string(),
84 name: "".to_string(),
85 };
86
87 let mut owner = None;
88 let mut nonce = None;
89
90 for e in it {
91 if e.is_empty() {
92 continue;
93 }
94
95 let e: Vec<&str> = e.split('(').collect();
97
98 if e.len() == 1 {
100 match e[0] {
101 "canRequestInvite" => {
102 typ = match typ {
103 InstanceType::Unknown => InstanceType::InvitePlus, InstanceType::Invite => InstanceType::InvitePlus, _ => Err(InstanceParseError::InvalidReqInvite(typ))?,
106 };
107 }
108 _ => Err(InstanceParseError::UnknownElement(e[0].to_string()))?,
109 };
110 continue;
111 }
112
113 let arg = &e[1];
115 let arg = &arg[..arg.len() - 1];
116 match e[0] {
117 "private" => {
118 let o = arg.strip_prefix("usr_").unwrap();
119 owner = Some(o.to_string());
120 typ = InstanceType::Invite;
121 }
122 "hidden" => {
123 let o = arg.strip_prefix("usr_").unwrap();
124 owner = Some(o.to_string());
125 typ = InstanceType::FriendPlus;
126 }
127 "friends" => {
128 let o = arg.strip_prefix("usr_").unwrap();
129 owner = Some(o.to_string());
130 typ = InstanceType::Friends;
131 }
132 "nonce" => {
133 nonce = Some(arg.to_string());
134 }
135 _ => Err(InstanceParseError::UnknownElement(e[0].to_string()))?,
136 }
137 }
138
139 Ok(Instance {
140 world,
141 id,
142 owner,
143 typ,
144 nonce,
145 })
146}
147
148impl From<&Vec<crate::LogEnum>> for InstanceLogList {
149 fn from(from: &Vec<crate::LogEnum>) -> InstanceLogList {
150 let mut v = Vec::new();
151 let mut log = from.iter();
152
153 let mut ilog: Option<InstanceLog> = None;
154 let mut world_name = None;
155 let mut enter = None;
156 loop {
157 let l = log.next();
158 if l.is_none() {
159 break;
160 }
161 let l = l.unwrap().as_log();
162 if l.is_none() {
163 continue;
164 }
165 let l = l.unwrap();
166 if l.typ != log::Type::RoomManager {
167 continue;
168 }
169
170 let msg = &l.msg[0];
171 if msg == "Clearing Room Metadata" || msg.starts_with("Room metadata") {
172 continue;
173 }
174
175 if let Some(name) = msg.strip_prefix("Entering Room: ") {
176 world_name = Some(name.to_string());
177 enter = Some(l.date);
178 continue;
179 }
180 if msg.starts_with("Joining wrld_") {
181 let msg = msg.strip_prefix("Joining ").unwrap();
182 let mut instance = parse_instance(msg).unwrap();
183 if let Some(ref name) = world_name {
184 instance.world.name = name.to_string();
185 }
186 ilog = Some(InstanceLog {
187 instance,
188 enter,
189 join: Some(l.date),
190 join_or_create: None,
191 joined: None,
192 left: None,
193 });
194 }
195
196 if let Some(name) = msg.strip_prefix("Joining or Creating Room: ") {
197 if let Some(ref mut ilog) = ilog {
198 let wn = &mut ilog.instance.world.name;
199 if wn.is_empty() {
200 *wn = name.to_string();
201 } else if wn != name {
202 panic!("somthing wrong");
203 }
204 ilog.join_or_create = Some(l.date);
205 }
206 }
207
208 if msg == "Successfully joined room" {
209 if let Some(ref mut ilog) = ilog {
210 ilog.joined = Some(l.date);
211 }
212 }
213
214 if msg == "Successfully left room" {
215 if let Some(mut ilog) = ilog {
216 ilog.left = Some(l.date);
217 v.push(ilog);
218 }
219 ilog = None;
220 }
221 }
222 if let Some(ilog) = ilog {
224 if v.is_empty() || v.last().unwrap().instance.id != ilog.instance.id {
225 v.push(ilog);
226 }
227 }
228
229 v.into()
230 }
231}
232
233impl Deref for InstanceLogList {
234 type Target = Vec<InstanceLog>;
235 fn deref(&self) -> &Self::Target {
236 &self.0
237 }
238}
239impl Into<InstanceLogList> for Vec<InstanceLog> {
240 fn into(self) -> InstanceLogList {
241 InstanceLogList(self)
242 }
243}