ts3_query/
data.rs

1use crate::raw::*;
2use crate::Result;
3use std::collections::HashMap;
4
5// Ts3 uses just whatever is available in the DB system, could be i32 or i64, though every foreign key is unsigned..
6pub type ServerId = u64;
7pub type ServerGroupID = u64;
8pub type ChannelId = u64;
9/// Temporary, per connection ID of a client, reused upon disconnect.  
10/// Not to be confused with a client database, myteamspeak or identity ID.
11pub type ClientId = u16;
12/// Server interal ID for client, not it's Identity / MyTeamspeak ID.
13pub type ClientDBId = u64;
14pub type ChannelGroupId = u64;
15/// CRC32 checksum of the channel icon, but received as i64 instead of u64, except when using `clientdbinfo`
16pub type IconHash = i64;
17
18/// Server Group returned from `server_group_list`. Field names are according to the query protocol.
19#[derive(Debug)]
20pub struct ServerGroup {
21    /// Identifier for this server group
22    pub sgid: ServerGroupID,
23    pub name: String,
24    /// Group type: template,regular,query
25    /// `type` use `r#type` to specify in rust
26    pub r#type: i32,
27    pub iconid: IconHash,
28    // whether group is stored to DB
29    pub savedb: bool,
30}
31
32impl ServerGroup {
33    /// Create struct from raw line-data assuming no unescaping was performed
34    pub(crate) fn from_raw(mut data: HashMap<String, Option<String>>) -> Result<Self> {
35        let sgid = int_val_parser(&mut data, "sgid")?;
36        let name = string_val_parser(&mut data, "name")?;
37        let r#type = int_val_parser(&mut data, "type")?;
38        let iconid = int_val_parser(&mut data, "iconid")?;
39        let savedb = bool_val_parser(&mut data, "savedb")?;
40
41        Ok(ServerGroup {
42            sgid,
43            name,
44            r#type,
45            iconid,
46            savedb,
47        })
48    }
49}
50
51#[derive(Debug)]
52pub struct OnlineClient {
53    pub clid: ClientId,
54    pub cid: ChannelId,
55    pub client_database_id: ClientDBId,
56    pub client_nickname: String,
57    /// 0 For normal client, 1 for query
58    pub client_type: i8,
59}
60
61impl OnlineClient {
62    pub(crate) fn from_raw(mut data: HashMap<String, Option<String>>) -> Result<Self> {
63        let clid = int_val_parser(&mut data, "clid")?;
64        let cid = int_val_parser(&mut data, "cid")?;
65        let client_database_id = int_val_parser(&mut data, "client_database_id")?;
66        let client_nickname: String = string_val_parser(&mut data, "client_nickname")?;
67        let client_type = int_val_parser(&mut data, "client_type")?;
68
69        Ok(OnlineClient {
70            clid,
71            cid,
72            client_database_id,
73            client_nickname,
74            client_type,
75        })
76    }
77}
78
79#[derive(Debug)]
80pub struct OnlineClientFull {
81    pub clid: ClientId,
82    pub cid: ChannelId,
83    pub client_database_id: ClientDBId,
84    pub client_nickname: String,
85    /// 0 For normal client, 1 for query
86    pub client_type: i8,
87    pub client_away: bool,
88    pub client_away_message: Option<String>,
89    pub client_flag_talking: bool,
90    pub client_input_muted: bool,
91    pub client_output_muted: bool,
92    pub client_input_hardware: bool,
93    pub client_output_hardware: bool,
94    pub client_talk_power: i32,
95    pub client_is_talker: bool,
96    pub client_is_priority_speaker: bool,
97    pub client_is_recording: bool,
98    pub client_is_channel_commander: bool,
99    pub client_unique_identifier: String,
100    pub client_servergroups: Vec<ServerGroupID>,
101    pub client_channel_group_id: ChannelGroupId,
102    pub client_channel_group_inherited_channel_id: ChannelGroupId,
103    pub client_version: String,
104    pub client_platform: String,
105    pub client_idle_time: i64,
106    pub client_created: i64,
107    pub client_lastconnected: i64,
108    pub client_country: String,
109    pub connection_client_ip: String,
110    pub client_badges: Option<String>, // TODO: CHECK TYPE
111}
112
113impl OnlineClientFull {
114    pub(crate) fn from_raw(mut data: HashMap<String, Option<String>>) -> Result<Self> {
115        let clid = int_val_parser(&mut data, "clid")?;
116        let cid = int_val_parser(&mut data, "cid")?;
117        let client_database_id = int_val_parser(&mut data, "client_database_id")?;
118        let client_nickname: String = string_val_parser(&mut data, "client_nickname")?;
119        let client_type = int_val_parser(&mut data, "client_type")?;
120
121        let client_away = bool_val_parser(&mut data, "client_away")?;
122        let client_away_message = string_val_parser_opt(&mut data, "client_away_message")?;
123        let client_flag_talking = bool_val_parser(&mut data, "client_flag_talking")?;
124        let client_input_muted = bool_val_parser(&mut data, "client_input_muted")?;
125        let client_output_muted = bool_val_parser(&mut data, "client_output_muted")?;
126        let client_input_hardware = bool_val_parser(&mut data, "client_input_hardware")?;
127        let client_output_hardware = bool_val_parser(&mut data, "client_output_hardware")?;
128        let client_talk_power = int_val_parser(&mut data, "client_talk_power")?;
129        let client_is_talker = bool_val_parser(&mut data, "client_is_talker")?;
130        let client_is_priority_speaker = bool_val_parser(&mut data, "client_is_priority_speaker")?;
131        let client_is_recording = bool_val_parser(&mut data, "client_is_recording")?;
132        let client_is_channel_commander =
133            bool_val_parser(&mut data, "client_is_channel_commander")?;
134        let client_unique_identifier = string_val_parser(&mut data, "client_unique_identifier")?;
135        let client_servergroups = int_list_val_parser(&mut data, "client_servergroups")?;
136        let client_channel_group_id = int_val_parser(&mut data, "client_channel_group_id")?;
137        let client_channel_group_inherited_channel_id =
138            int_val_parser(&mut data, "client_channel_group_inherited_channel_id")?;
139        let client_version = string_val_parser(&mut data, "client_version")?;
140        let client_platform = string_val_parser(&mut data, "client_platform")?;
141        let client_idle_time = int_val_parser(&mut data, "client_idle_time")?;
142        let client_created = int_val_parser(&mut data, "client_created")?;
143        let client_lastconnected = int_val_parser(&mut data, "client_lastconnected")?;
144        let client_country = string_val_parser(&mut data, "client_country")?;
145        let connection_client_ip = string_val_parser(&mut data, "connection_client_ip")?;
146        let client_badges = string_val_parser_opt(&mut data, "client_badges")?;
147
148        Ok(OnlineClientFull {
149            clid,
150            cid,
151            client_database_id,
152            client_nickname,
153            client_type,
154            client_away,
155            client_away_message,
156            client_flag_talking,
157            client_input_muted,
158            client_output_muted,
159            client_input_hardware,
160            client_output_hardware,
161            client_talk_power,
162            client_is_talker,
163            client_is_priority_speaker,
164            client_is_recording,
165            client_is_channel_commander,
166            client_unique_identifier,
167            client_servergroups,
168            client_channel_group_id,
169            client_channel_group_inherited_channel_id,
170            client_version,
171            client_platform,
172            client_idle_time,
173            client_created,
174            client_lastconnected,
175            client_country,
176            connection_client_ip,
177            client_badges,
178        })
179    }
180}
181
182#[derive(Debug)]
183pub struct Channel {
184    /// Channel ID
185    pub cid: ChannelId,
186    /// Channel parent, 0 for server
187    pub pid: ChannelId,
188    /// ID of the channel
189    pub channel_order: ChannelId,
190    pub channel_name: String,
191    pub total_clients: i32,
192    pub channel_needed_subscribe_power: i32,
193}
194
195impl Channel {
196    pub(crate) fn from_raw(mut data: HashMap<String, Option<String>>) -> Result<Self> {
197        let cid = int_val_parser(&mut data, "cid")?;
198        let pid = int_val_parser(&mut data, "pid")?;
199        let channel_order = int_val_parser(&mut data, "channel_order")?;
200        let channel_name: String = string_val_parser(&mut data, "channel_name")?;
201        let total_clients = int_val_parser(&mut data, "total_clients")?;
202        let channel_needed_subscribe_power =
203            int_val_parser(&mut data, "channel_needed_subscribe_power")?;
204
205        Ok(Channel {
206            cid,
207            pid,
208            channel_order,
209            channel_name,
210            total_clients,
211            channel_needed_subscribe_power,
212        })
213    }
214}
215
216#[derive(Debug)]
217pub struct ChannelFull {
218    /// Channel ID
219    pub cid: ChannelId,
220    /// Channel parent
221    pub pid: ChannelId,
222    /// ID of the channel
223    pub channel_order: ChannelId,
224    pub channel_name: String,
225    pub total_clients: i32,
226    pub channel_needed_subscribe_power: i32,
227    pub channel_topic: Option<String>,
228    pub channel_flag_default: bool,
229    pub channel_flag_password: bool,
230    pub channel_flag_permanent: bool,
231    pub channel_flag_semi_permanent: bool,
232    pub channel_codec: i32,
233    pub channel_codec_quality: u8,
234    pub channel_needed_talk_power: i32,
235    pub channel_icon_id: IconHash,
236    pub seconds_empty: i64,
237    pub total_clients_family: i32,
238    pub channel_maxclients: i32,
239    pub channel_maxfamilyclients: i32,
240}
241
242impl ChannelFull {
243    pub(crate) fn from_raw(mut data: HashMap<String, Option<String>>) -> Result<Self> {
244        let cid = int_val_parser(&mut data, "cid")?;
245        let pid = int_val_parser(&mut data, "pid")?;
246        let channel_order = int_val_parser(&mut data, "channel_order")?;
247        let channel_name: String = string_val_parser(&mut data, "channel_name")?;
248        let total_clients = int_val_parser(&mut data, "total_clients")?;
249        let channel_needed_subscribe_power =
250            int_val_parser(&mut data, "channel_needed_subscribe_power")?;
251
252        let channel_topic = string_val_parser_opt(&mut data, "channel_topic")?;
253        let channel_flag_default = bool_val_parser(&mut data, "channel_flag_default")?;
254        let channel_flag_password = bool_val_parser(&mut data, "channel_flag_password")?;
255        let channel_flag_permanent = bool_val_parser(&mut data, "channel_flag_permanent")?;
256        let channel_flag_semi_permanent =
257            bool_val_parser(&mut data, "channel_flag_semi_permanent")?;
258        let channel_codec = int_val_parser(&mut data, "channel_codec")?;
259        let channel_codec_quality = int_val_parser(&mut data, "channel_codec_quality")?;
260        let channel_needed_talk_power = int_val_parser(&mut data, "channel_needed_talk_power")?;
261        let channel_icon_id = int_val_parser(&mut data, "channel_icon_id")?;
262        let seconds_empty = int_val_parser(&mut data, "seconds_empty")?;
263        let total_clients_family = int_val_parser(&mut data, "total_clients_family")?;
264        let channel_maxclients = int_val_parser(&mut data, "channel_maxclients")?;
265        let channel_maxfamilyclients = int_val_parser(&mut data, "channel_maxfamilyclients")?;
266
267        Ok(ChannelFull {
268            cid,
269            pid,
270            channel_order,
271            channel_name,
272            total_clients,
273            channel_needed_subscribe_power,
274            channel_topic,
275            channel_flag_default,
276            channel_flag_password,
277            channel_flag_permanent,
278            channel_flag_semi_permanent,
279            channel_codec,
280            channel_codec_quality,
281            channel_needed_talk_power,
282            channel_icon_id,
283            seconds_empty,
284            total_clients_family,
285            channel_maxclients,
286            channel_maxfamilyclients,
287        })
288    }
289}
290
291/// This struct defines the values of a channel that are changeable
292/// The difference to [ChannelFull] is that this does not contain values that are not changeable like
293#[derive(Debug, Default)]
294pub struct ChannelEdit {
295    /// The channel name
296    ///
297    /// **Note** Has to be unique or else it might fail!
298    pub channel_name: Option<String>,
299    /// See [ChannelLife]
300    pub channel_life: Option<ChannelLife>,
301    /// The parent channel id.
302    /// If set, the channel becomes a Sub-Channel
303    pub pid: Option<ChannelId>,
304    /// The channel after which this channel gets placed
305    pub channel_order: Option<ChannelId>,
306    pub channel_topic: Option<String>,
307    pub channel_password: Option<String>,
308    pub channel_maxclients: Option<i32>,
309    pub channel_maxfamilyclients: Option<i32>,
310    pub channel_flag_default: bool,
311    pub channel_codec: Option<i32>,
312    pub channel_codec_quality: Option<u8>,
313    pub channel_needed_talk_power: Option<i32>,
314    pub channel_icon_id: Option<IconHash>,
315}
316
317/// This defines when/if the channel gets automatically removed
318#[derive(Debug)]
319pub enum ChannelLife {
320    /// Permanent channel
321    Permanent,
322    /// Semi-Permanent channel (gets removed after server restart)
323    SemiPermanent,
324    /// Temporary channel (gets removed if empty)
325    Temporary,
326}
327
328impl ChannelEdit {
329    pub(crate) fn to_raw(&self) -> String {
330        let mut result = String::new();
331
332        if let Some(x) = &self.channel_name {
333            result += &format!(" channel_name={}", &escape_arg(x));
334        }
335        if let Some(x) = &self.channel_life {
336            match x {
337                ChannelLife::Permanent => result += &format!(" channel_flag_permanent={}", 1),
338                ChannelLife::SemiPermanent => {
339                    result += &format!(" channel_flag_semi_permanent={}", 1)
340                }
341                ChannelLife::Temporary => result += &format!(" channel_flag_temporary={}", 1),
342            }
343        }
344        if let Some(x) = self.pid {
345            result += &format!(" cpid={}", x);
346        }
347        if let Some(x) = self.channel_order {
348            result += &format!(" channel_order={}", x);
349        }
350        if let Some(x) = &self.channel_topic {
351            result += &format!(" channel_topic={}", &escape_arg(x));
352        }
353        if let Some(x) = &self.channel_password {
354            result += &format!(" channel_password={}", &escape_arg(x));
355        }
356        if let Some(x) = self.channel_maxclients {
357            result += &format!(" channel_maxclients={}", x);
358        }
359        if let Some(x) = self.channel_maxfamilyclients {
360            result += &format!(" channel_maxfamilyclients={}", x);
361        }
362        if self.channel_flag_default {
363            result += &format!(" channel_flag_default={}", 1);
364        }
365        if let Some(x) = self.channel_codec {
366            result += &format!(" channel_codec={}", x);
367        }
368        if let Some(x) = self.channel_codec_quality {
369            result += &format!(" channel_codec_quality={}", x);
370        }
371        if let Some(x) = self.channel_needed_talk_power {
372            result += &format!(" channel_needed_talk_power={}", x);
373        }
374        if let Some(x) = self.channel_icon_id {
375            result += &format!(" channel_icon_id={}", x);
376        }
377
378        result
379    }
380}
381
382impl From<&ChannelFull> for ChannelEdit {
383    fn from(c: &ChannelFull) -> Self {
384        let channel_life;
385        if c.channel_flag_permanent {
386            channel_life = ChannelLife::Permanent;
387        } else if c.channel_flag_semi_permanent {
388            channel_life = ChannelLife::SemiPermanent;
389        } else {
390            channel_life = ChannelLife::Temporary;
391        }
392
393        Self {
394            channel_name: c.channel_name.clone().into(),
395            channel_life: Some(channel_life),
396            pid: c.pid.into(),
397            channel_order: c.channel_order.into(),
398            channel_topic: c.channel_topic.clone(),
399            channel_password: None,
400            channel_maxclients: c.channel_maxclients.into(),
401            channel_maxfamilyclients: c.channel_maxfamilyclients.into(),
402            channel_flag_default: c.channel_flag_default,
403            channel_codec: c.channel_codec.into(),
404            channel_codec_quality: c.channel_codec_quality.into(),
405            channel_needed_talk_power: c.channel_needed_talk_power.into(),
406            channel_icon_id: c.channel_icon_id.into(),
407        }
408    }
409}
410
411impl Default for ChannelLife {
412    fn default() -> Self {
413        Self::Temporary
414    }
415}
416
417/// Server error response
418#[derive(Debug)]
419pub struct ErrorResponse {
420    /// Error ID
421    pub id: usize,
422    /// Error message
423    pub msg: String,
424}
425
426impl ErrorResponse {
427    // courtesy of https://yat.qa/resources/server-error-codes/
428    /// Returns error name if existing
429    pub fn error_name(&self) -> Option<&'static str> {
430        match self.id {
431            0 => Some("unknown error code"),
432            1 => Some("undefined error"),
433            2 => Some("not implemented"),
434            5 => Some("library time limit reached"),
435            256 => Some("command not found"),
436            257 => Some("unable to bind network port"),
437            258 => Some("no network port available"),
438            512 => Some("invalid clientID"),
439            513 => Some("nickname is already in use"),
440            514 => Some("invalid error code"),
441            515 => Some("max clients protocol limit reached"),
442            516 => Some("invalid client type"),
443            517 => Some("already subscribed"),
444            518 => Some("not logged in"),
445            519 => Some("could not validate client identity"),
446            520 => Some("invalid loginname or password"),
447            521 => Some("too many clones already connected"),
448            522 => Some("client version outdated, please update"),
449            523 => Some("client is online"),
450            524 => Some("client is flooding"),
451            525 => Some("client is modified"),
452            526 => Some("can not verify client at this moment"),
453            527 => Some("client is not permitted to log in"),
454            528 => Some("client is not subscribed to the channel"),
455            768 => Some("invalid channelID"),
456            769 => Some("max channels protocol limit reached"),
457            770 => Some("already member of channel"),
458            771 => Some("channel name is already in use"),
459            772 => Some("channel not empty"),
460            773 => Some("can not delete default channel"),
461            774 => Some("default channel requires permanent"),
462            775 => Some("invalid channel flags"),
463            776 => Some("permanent channel can not be child of non permanent channel"),
464            777 => Some("channel maxclient reached"),
465            778 => Some("channel maxfamily reached"),
466            779 => Some("invalid channel order"),
467            780 => Some("channel does not support filetransfers"),
468            781 => Some("invalid channel password"),
469            782 => Some("channel is private channel"),
470            783 => Some("invalid security hash supplied by client"),
471            1024 => Some("invalid serverID"),
472            1025 => Some("server is running"),
473            1026 => Some("server is shutting down"),
474            1027 => Some("server maxclient reached"),
475            1028 => Some("invalid server password"),
476            1029 => Some("deployment active"),
477            1030 => Some("unable to stop own server in your connection class"),
478            1031 => Some("server is virtual"),
479            1032 => Some("server wrong machineID"),
480            1033 => Some("server is not running"),
481            1034 => Some("server is booting up"),
482            1035 => Some("server got an invalid status for this operation"),
483            1036 => Some("server modal quit"),
484            1037 => Some("server version is too old for command"),
485            1040 => Some("server blacklisted"),
486            1280 => Some("database error"),
487            1281 => Some("database empty result set"),
488            1282 => Some("database duplicate entry"),
489            1283 => Some("database no modifications"),
490            1284 => Some("database invalid constraint"),
491            1285 => Some("database reinvoke command"),
492            1536 => Some("invalid quote"),
493            1537 => Some("invalid parameter count"),
494            1538 => Some("invalid parameter"),
495            1539 => Some("parameter not found"),
496            1540 => Some("convert error"),
497            1541 => Some("invalid parameter size"),
498            1542 => Some("missing required parameter"),
499            1543 => Some("invalid checksum"),
500            1792 => Some("virtual server got a critical error"),
501            1793 => Some("Connection lost"),
502            1794 => Some("not connected"),
503            1795 => Some("no cached connection info"),
504            1796 => Some("currently not possible"),
505            1797 => Some("failed connection initialization"),
506            1798 => Some("could not resolve hostname"),
507            1799 => Some("invalid server connection handler ID"),
508            1800 => Some("could not initialize Input Manager"),
509            1801 => Some("client library not initialized"),
510            1802 => Some("server library not initialized"),
511            1803 => Some("too many whisper targets"),
512            1804 => Some("no whisper targets found"),
513            2048 => Some("invalid file name"),
514            2049 => Some("invalid file permissions"),
515            2050 => Some("file already exists"),
516            2051 => Some("file not found"),
517            2052 => Some("file input/output error"),
518            2053 => Some("invalid file transfer ID"),
519            2054 => Some("invalid file path"),
520            2055 => Some("no files available"),
521            2056 => Some("overwrite excludes resume"),
522            2057 => Some("invalid file size"),
523            2058 => Some("file already in use"),
524            2059 => Some("could not open file transfer connection"),
525            2060 => Some("no space left on device (disk full?)"),
526            2061 => Some("file exceeds file system's maximum file size"),
527            2062 => Some("file transfer connection timeout"),
528            2063 => Some("lost file transfer connection"),
529            2064 => Some("file exceeds supplied file size"),
530            2065 => Some("file transfer complete"),
531            2066 => Some("file transfer canceled"),
532            2067 => Some("file transfer interrupted"),
533            2068 => Some("file transfer server quota exceeded"),
534            2069 => Some("file transfer client quota exceeded"),
535            2070 => Some("file transfer reset"),
536            2071 => Some("file transfer limit reached"),
537            2304 => Some("preprocessor disabled"),
538            2305 => Some("internal preprocessor"),
539            2306 => Some("internal encoder"),
540            2307 => Some("internal playback"),
541            2308 => Some("no capture device available"),
542            2309 => Some("no playback device available"),
543            2310 => Some("could not open capture device"),
544            2311 => Some("could not open playback device"),
545            2312 => Some("ServerConnectionHandler has a device registered"),
546            2313 => Some("invalid capture device"),
547            2314 => Some("invalid clayback device"),
548            2315 => Some("invalid wave file"),
549            2316 => Some("wave file type not supported"),
550            2317 => Some("could not open wave file"),
551            2318 => Some("internal capture"),
552            2319 => Some("device still in use"),
553            2320 => Some("device already registerred"),
554            2321 => Some("device not registered/known"),
555            2322 => Some("unsupported frequency"),
556            2323 => Some("invalid channel count"),
557            2324 => Some("read error in wave"),
558            2325 => Some("sound need more data"),
559            2326 => Some("sound device was busy"),
560            2327 => Some("there is no sound data for this period"),
561            2328 => {
562                Some("Channelmask set bits count (speakers) is not the same as channel (count)")
563            }
564            2560 => Some("invalid group ID"),
565            2561 => Some("duplicate entry"),
566            2562 => Some("invalid permission ID"),
567            2563 => Some("empty result set"),
568            2564 => Some("access to default group is forbidden"),
569            2565 => Some("invalid size"),
570            2566 => Some("invalid value"),
571            2567 => Some("group is not empty"),
572            2568 => Some("insufficient client permissions"),
573            2569 => Some("insufficient group modify power"),
574            2570 => Some("insufficient permission modify power"),
575            2571 => Some("template group is currently used"),
576            2572 => Some("permission error"),
577            2816 => Some("virtualserver limit reached"),
578            2817 => Some("max slot limit reached"),
579            2818 => Some("license file not found"),
580            2819 => Some("license date not ok"),
581            2820 => Some("unable to connect to accounting server"),
582            2821 => Some("unknown accounting error"),
583            2822 => Some("accounting server error"),
584            2823 => Some("instance limit reached"),
585            2824 => Some("instance check error"),
586            2825 => Some("license file invalid"),
587            2826 => Some("virtualserver is running elsewhere"),
588            2827 => Some("virtualserver running in same instance already"),
589            2828 => Some("virtualserver already started"),
590            2829 => Some("virtualserver not started"),
591            3072 => Some("invalid message id"),
592            3328 => Some("invalid ban id"),
593            3329 => Some("connection failed, you are banned"),
594            3330 => Some("rename failed, new name is banned"),
595            3331 => Some("flood ban"),
596            3584 => Some("unable to initialize tts"),
597            3840 => Some("invalid privilege key"),
598            4352 => Some("invalid password"),
599            4353 => Some("invalid request"),
600            4354 => Some("no (more) slots available"),
601            4355 => Some("pool missing"),
602            4356 => Some("pool unknown"),
603            4357 => Some("unknown ip location (perhaps LAN ip?)"),
604            4358 => Some("internal error (tried exceeded)"),
605            4359 => Some("too many slots requested"),
606            4360 => Some("too many reserved"),
607            4361 => Some("could not connect to provisioning server"),
608            4368 => Some("authentication server not connected"),
609            4369 => Some("authentication data too large"),
610            4370 => Some("already initialized"),
611            4371 => Some("not initialized"),
612            4372 => Some("already connecting"),
613            4373 => Some("already connected"),
614            4375 => Some("io_error"),
615            _ => None,
616        }
617    }
618}
619
620impl std::fmt::Display for ErrorResponse {
621    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
622        if let Some(descr) = self.error_name() {
623            writeln!(f, "Error {}: {}, msg: {}", self.id, descr, self.msg)
624        } else {
625            writeln!(f, "Unknown Error code {}, msg: {}", self.id, self.msg)
626        }
627    }
628}