cc_utils/
errors.rs

1//! Implementation of optional private errors for `salvo` and client errors for `reqwest`.
2
3#[cfg(feature = "salvo")]
4#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
5use std::any::Any;
6
7#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
8use std::fmt::Formatter;
9
10#[cfg(feature = "salvo")]
11use salvo::http::StatusCode;
12
13#[cfg(feature = "salvo")]
14use salvo::oapi::{EndpointOutRegister, ToSchema};
15
16#[cfg(feature = "salvo")]
17use salvo::{Depot, Request, Response};
18
19#[cfg(feature = "salvo")]
20use salvo::Writer as ServerResponseWriter;
21
22pub type BoxDynError = Box<dyn std::error::Error + 'static + Send + Sync>;
23
24/// Data structure responsible for server errors.
25#[derive(Debug)]
26pub struct ErrorResponse {
27  #[cfg(feature = "salvo")]
28  #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
29  pub status_code: Option<StatusCode>,
30  pub error_text: String,
31  pub original_text: Option<String>,
32  pub public_error: bool,
33}
34
35/// Data structure responsible for client errors.
36#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
37#[derive(Debug)]
38pub struct CliError {
39  pub message: String,
40}
41
42#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
43impl std::fmt::Display for CliError {
44  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45    f.write_str(&format!("{}", self.message))
46  }
47}
48
49#[cfg(feature = "salvo")]
50#[salvo::async_trait]
51impl ServerResponseWriter for ErrorResponse {
52  /// Method for sending an error message to the client.
53  async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
54    res.status_code(self.status_code.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR));
55    if !self.public_error {
56      let public_error_desc = match self.status_code {
57        Some(StatusCode::BAD_REQUEST) => "Bad request.",
58        Some(StatusCode::UNAUTHORIZED) => "Unauthorized request.",
59        Some(StatusCode::FORBIDDEN) => "Access denied.",
60        Some(StatusCode::NOT_FOUND) => "Page or method not found.",
61        Some(StatusCode::METHOD_NOT_ALLOWED) => "Method not allowed.",
62        Some(StatusCode::LOCKED) => "Your actions is locked.",
63        Some(StatusCode::INTERNAL_SERVER_ERROR) => {
64          "Internal server error. Contact the administrator."
65        }
66        _ => "Specific error. Check with the administrator for details.",
67      };
68      log::error!(
69        "Error with code {:?}: \"{}\", client will get: \"{}\"",
70        self.status_code,
71        self.error_text,
72        public_error_desc
73      );
74      if self.original_text.is_some() {
75        log::error!("The original error text: {:?}", self.original_text.unwrap());
76      }
77      res.render(public_error_desc);
78    } else {
79      log::error!(
80        "Error with code {:?}: \"{}\"",
81        self.status_code,
82        self.error_text
83      );
84      if self.original_text.is_some() {
85        log::error!("The original error text: {:?}", self.original_text.unwrap());
86      }
87      res.render(&self.error_text);
88    }
89  }
90}
91
92#[cfg(feature = "salvo")]
93impl EndpointOutRegister for ErrorResponse {
94  /// Registers error types for OpenAPI.
95  fn register(components: &mut salvo::oapi::Components, operation: &mut salvo::oapi::Operation) {
96    operation.responses.insert(
97      "400",
98      salvo::oapi::Response::new("Bad request")
99        .add_content("text/plain", String::to_schema(components)),
100    );
101    operation.responses.insert(
102      "401",
103      salvo::oapi::Response::new("Unauthorized")
104        .add_content("text/plain", String::to_schema(components)),
105    );
106    operation.responses.insert(
107      "403",
108      salvo::oapi::Response::new("Forbidden")
109        .add_content("text/plain", String::to_schema(components)),
110    );
111    operation.responses.insert(
112      "404",
113      salvo::oapi::Response::new("Not found")
114        .add_content("text/plain", String::to_schema(components)),
115    );
116    operation.responses.insert(
117      "405",
118      salvo::oapi::Response::new("Method not allowed")
119        .add_content("text/plain", String::to_schema(components)),
120    );
121    operation.responses.insert(
122      "423",
123      salvo::oapi::Response::new("Locked")
124        .add_content("text/plain", String::to_schema(components)),
125    );
126    operation.responses.insert(
127      "500",
128      salvo::oapi::Response::new("Internal server error")
129        .add_content("text/plain", String::to_schema(components)),
130    );
131  }
132}
133
134#[cfg(feature = "salvo")]
135#[allow(dead_code)]
136impl ErrorResponse {
137  /// Private error BAD REQUEST (400).
138  pub fn with_400(&mut self) -> &mut Self {
139    self.status_code = Some(StatusCode::BAD_REQUEST);
140    self.public_error = false;
141    self
142  }
143
144  /// Public error BAD REQUEST (400).
145  pub fn with_400_pub(&mut self) -> &mut Self {
146    self.status_code = Some(StatusCode::BAD_REQUEST);
147    self.public_error = true;
148    self
149  }
150
151  /// Private error UNAUTHORIZED (401).
152  pub fn with_401(&mut self) -> &mut Self {
153    self.status_code = Some(StatusCode::UNAUTHORIZED);
154    self.public_error = false;
155    self
156  }
157
158  /// Public error UNAUTHORIZED (401).
159  pub fn with_401_pub(&mut self) -> &mut Self {
160    self.status_code = Some(StatusCode::UNAUTHORIZED);
161    self.public_error = true;
162    self
163  }
164
165  /// Private error FORBIDDEN (403).
166  pub fn with_403(&mut self) -> &mut Self {
167    self.status_code = Some(StatusCode::FORBIDDEN);
168    self.public_error = false;
169    self
170  }
171
172  /// Public error FORBIDDEN (403).
173  pub fn with_403_pub(&mut self) -> &mut Self {
174    self.status_code = Some(StatusCode::FORBIDDEN);
175    self.public_error = true;
176    self
177  }
178
179  /// Private error NOT FOUND (404).
180  pub fn with_404(&mut self) -> &mut Self {
181    self.status_code = Some(StatusCode::NOT_FOUND);
182    self.public_error = false;
183    self
184  }
185
186  /// Public error NOT FOUND (404).
187  pub fn with_404_pub(&mut self) -> &mut Self {
188    self.status_code = Some(StatusCode::NOT_FOUND);
189    self.public_error = true;
190    self
191  }
192
193  /// Private error METHOD NOT ALLOWED (405).
194  pub fn with_405(&mut self) -> &mut Self {
195    self.status_code = Some(StatusCode::METHOD_NOT_ALLOWED);
196    self.public_error = false;
197    self
198  }
199
200  /// Public error METHOD NOT ALLOWED (405).
201  pub fn with_405_pub(&mut self) -> &mut Self {
202    self.status_code = Some(StatusCode::METHOD_NOT_ALLOWED);
203    self.public_error = true;
204    self
205  }
206
207  /// Private error LOCKED (423).
208  pub fn with_423(&mut self) -> &mut Self {
209    self.status_code = Some(StatusCode::LOCKED);
210    self.public_error = false;
211    self
212  }
213
214  /// Public error LOCKED (423).
215  pub fn with_423_pub(&mut self) -> &mut Self {
216    self.status_code = Some(StatusCode::LOCKED);
217    self.public_error = true;
218    self
219  }
220
221  /// Private error INTERNAL SERVER ERROR (500).
222  pub fn with_500(&mut self) -> &mut Self {
223    self.status_code = Some(StatusCode::INTERNAL_SERVER_ERROR);
224    self.public_error = false;
225    self
226  }
227
228  /// Public error INTERNAL SERVER ERROR (500).
229  pub fn with_500_pub(&mut self) -> &mut Self {
230    self.status_code = Some(StatusCode::INTERNAL_SERVER_ERROR);
231    self.public_error = true;
232    self
233  }
234
235  /// Changes error message text.
236  pub fn with_text(&mut self, text: String) -> &mut Self {
237    match self.original_text {
238      None => self.original_text = Some(self.error_text.to_owned()),
239      Some(_) => {}
240    }
241    self.error_text = text;
242    self
243  }
244
245  /// Builds the response.
246  pub fn build(&mut self) -> Self {
247    Self {
248      status_code: self.status_code,
249      error_text: self.error_text.to_owned(),
250      original_text: self.original_text.clone(),
251      public_error: self.public_error,
252    }
253  }
254}
255
256/// A trait that allows you to transform any error into an `ErrorResponse` by assigning additional parameters.
257#[cfg(feature = "salvo")]
258pub trait Consider<T> {
259  fn consider(
260    self,
261    status_code: Option<StatusCode>,
262    error_text_replacement: Option<String>,
263    public: bool,
264  ) -> Result<T, ErrorResponse>;
265}
266
267#[cfg(feature = "reqwest")]
268#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
269pub trait ConsiderCli<T> {
270  fn consider_cli(self, error_text_replacement: Option<String>) -> Result<T, CliError>;
271}
272
273#[cfg(feature = "salvo")]
274impl<T> Consider<T> for Result<T, ErrorResponse> {
275  /// Changes the parameters of a possible error to the specified ones.
276  fn consider(
277    self,
278    status_code: Option<StatusCode>,
279    error_text_replacement: Option<String>,
280    public: bool,
281  ) -> Result<T, ErrorResponse> {
282    self.map_err(|e| {
283      let mut new_error = ErrorResponse {
284        status_code,
285        error_text: e.error_text,
286        original_text: e.original_text,
287        public_error: public,
288      };
289      if error_text_replacement.is_some() {
290        new_error.original_text = Some(new_error.error_text.to_owned());
291        new_error.error_text = error_text_replacement.unwrap();
292      }
293      new_error
294    })
295  }
296}
297
298#[cfg(feature = "reqwest")]
299#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
300impl<T> ConsiderCli<T> for Result<T, CliError> {
301  /// Changes the parameters of a possible error to the specified ones.
302  fn consider_cli(self, error_text_replacement: Option<String>) -> Result<T, CliError> {
303    self.map_err(|e| {
304      let mut new_error = CliError { message: e.message };
305      if error_text_replacement.is_some() {
306        new_error.message = error_text_replacement.unwrap();
307      }
308      new_error
309    })
310  }
311}
312
313#[cfg(feature = "salvo")]
314impl<T> Consider<T> for Result<T, String> {
315  /// Changes the parameters of a possible error to the specified ones.
316  fn consider(
317    self,
318    status_code: Option<StatusCode>,
319    error_text_replacement: Option<String>,
320    public: bool,
321  ) -> Result<T, ErrorResponse> {
322    self.map_err(|e| {
323      let mut new_error = ErrorResponse {
324        status_code,
325        error_text: e,
326        original_text: None,
327        public_error: public,
328      };
329      if error_text_replacement.is_some() {
330        new_error.original_text = Some(new_error.error_text.to_owned());
331        new_error.error_text = error_text_replacement.unwrap();
332      }
333      new_error
334    })
335  }
336}
337
338#[cfg(feature = "reqwest")]
339#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
340impl<T> ConsiderCli<T> for Result<T, String> {
341  /// Changes the parameters of a possible error to the specified ones.
342  fn consider_cli(self, error_text_replacement: Option<String>) -> Result<T, CliError> {
343    self.map_err(|e| {
344      let mut new_error = CliError { message: e };
345      if error_text_replacement.is_some() {
346        new_error.message = error_text_replacement.unwrap();
347      }
348      new_error
349    })
350  }
351}
352
353#[cfg(feature = "salvo")]
354impl<T> Consider<T> for Result<T, &str> {
355  /// Changes the parameters of a possible error to the specified ones.
356  fn consider(
357    self,
358    status_code: Option<StatusCode>,
359    error_text_replacement: Option<String>,
360    public: bool,
361  ) -> Result<T, ErrorResponse> {
362    self.map_err(|e| {
363      let mut new_error = ErrorResponse {
364        status_code,
365        error_text: e.to_owned(),
366        original_text: None,
367        public_error: public,
368      };
369      if error_text_replacement.is_some() {
370        new_error.original_text = Some(new_error.error_text.to_owned());
371        new_error.error_text = error_text_replacement.unwrap();
372      }
373      new_error
374    })
375  }
376}
377
378#[cfg(feature = "reqwest")]
379#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
380impl<T> ConsiderCli<T> for Result<T, &str> {
381  /// Changes the parameters of a possible error to the specified ones.
382  fn consider_cli(self, error_text_replacement: Option<String>) -> Result<T, CliError> {
383    self.map_err(|e| {
384      let mut new_error = CliError {
385        message: e.to_owned(),
386      };
387      if error_text_replacement.is_some() {
388        new_error.message = error_text_replacement.unwrap();
389      }
390      new_error
391    })
392  }
393}
394
395#[cfg(feature = "salvo")]
396impl From<String> for ErrorResponse {
397  /// Creates a new error from a string.
398  fn from(value: String) -> Self {
399    Self {
400      status_code: None,
401      error_text: value,
402      original_text: None,
403      public_error: false,
404    }
405  }
406}
407
408#[cfg(feature = "reqwest")]
409#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
410impl From<String> for CliError {
411  /// Creates a new error from a string.
412  fn from(value: String) -> Self {
413    Self { message: value }
414  }
415}
416
417#[cfg(feature = "salvo")]
418impl From<&str> for ErrorResponse {
419  /// Creates a new error from a string.
420  fn from(value: &str) -> Self {
421    Self {
422      status_code: None,
423      error_text: value.to_owned(),
424      original_text: None,
425      public_error: false,
426    }
427  }
428}
429
430#[cfg(feature = "reqwest")]
431#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
432impl From<&str> for CliError {
433  /// Creates a new error from a string.
434  fn from(value: &str) -> Self {
435    Self {
436      message: value.to_owned(),
437    }
438  }
439}
440
441/// Macro to simplify `Consider` trait implementation.
442#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
443macro_rules! impl_consider {
444  ($e:ty) => {
445    #[cfg(feature = "salvo")]
446    impl<T> Consider<T> for Result<T, $e> {
447      /// Изменяет параметры возможной ошибки на указанные.
448      fn consider(
449        self,
450        status_code: Option<StatusCode>,
451        error_text_replacement: Option<String>,
452        public: bool,
453      ) -> Result<T, ErrorResponse> {
454        self.map_err(|e| {
455          let mut new_error = ErrorResponse {
456            status_code,
457            error_text: e.to_string(),
458            original_text: None,
459            public_error: public,
460          };
461          if error_text_replacement.is_some() {
462            new_error.original_text = Some(new_error.error_text.to_owned());
463            new_error.error_text = error_text_replacement.unwrap();
464          }
465          new_error
466        })
467      }
468    }
469
470    #[cfg(feature = "salvo")]
471    impl From<$e> for ErrorResponse {
472      /// Создаёт `ErrorResponse` из данной ошибки.
473      fn from(value: $e) -> Self {
474        value.to_string().into()
475      }
476    }
477  };
478}
479
480/// Macro to simplify `ConsiderCli` trait implementation.
481#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
482macro_rules! impl_consider_cli {
483  ($e:ty) => {
484    #[cfg(feature = "reqwest")]
485    impl<T> ConsiderCli<T> for Result<T, $e> {
486      /// Изменяет параметры возможной ошибки на указанные.
487      fn consider_cli(self, error_text_replacement: Option<String>) -> Result<T, CliError> {
488        self.map_err(|e| {
489          let mut new_error = CliError {
490            message: e.to_string(),
491          };
492          if error_text_replacement.is_some() {
493            new_error.message = error_text_replacement.unwrap();
494          }
495          new_error
496        })
497      }
498    }
499
500    #[cfg(feature = "reqwest")]
501    impl From<$e> for CliError {
502      /// Создаёт `CliError` из данной ошибки.
503      fn from(value: $e) -> Self {
504        value.to_string().into()
505      }
506    }
507  };
508}
509
510#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
511impl_consider!(rmp_serde::encode::Error);
512#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
513impl_consider!(rmp_serde::decode::Error);
514#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
515impl_consider!(std::io::Error);
516#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
517impl_consider!(std::string::FromUtf8Error);
518#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
519impl_consider!(std::env::VarError);
520#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
521impl_consider!(std::sync::mpsc::RecvError);
522#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
523impl_consider!(log::SetLoggerError);
524#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
525impl_consider!(serde_json::Error);
526#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
527impl_consider!(BoxDynError);
528
529#[cfg(feature = "salvo")]
530#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
531impl_consider!(salvo::Error);
532
533#[cfg(feature = "salvo")]
534#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
535impl_consider!(salvo::hyper::http::status::InvalidStatusCode);
536
537#[cfg(feature = "salvo")]
538#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
539impl_consider!(salvo::http::ParseError);
540
541#[cfg(feature = "salvo")]
542#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
543impl<T> Consider<T> for Result<T, Option<&Box<dyn Any + Send + Sync>>> {
544  /// Изменяет параметры возможной ошибки на указанные.
545  fn consider(
546    self,
547    status_code: Option<StatusCode>,
548    error_text_replacement: Option<String>,
549    public: bool,
550  ) -> Result<T, ErrorResponse> {
551    self.map_err(|_| {
552      let mut new_error = ErrorResponse {
553        status_code,
554        error_text: "Depot obtain failed!".into(),
555        original_text: None,
556        public_error: public,
557      };
558      if error_text_replacement.is_some() {
559        new_error.original_text = Some(new_error.error_text.to_owned());
560        new_error.error_text = error_text_replacement.unwrap();
561      }
562      new_error
563    })
564  }
565}
566
567#[cfg(feature = "salvo")]
568#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
569impl From<Option<&Box<dyn Any + Send + Sync>>> for ErrorResponse {
570  /// Создаёт `ErrorResponse` из данной ошибки.
571  fn from(_value: Option<&Box<(dyn Any + Send + Sync + 'static)>>) -> Self {
572    "Depot obtain failed!".into()
573  }
574}
575
576#[cfg(feature = "salvo")]
577#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
578impl<T, U> Consider<T> for Result<T, std::sync::mpsc::SendError<U>> {
579  /// Изменяет параметры возможной ошибки на указанные.
580  fn consider(
581    self,
582    status_code: Option<StatusCode>,
583    error_text_replacement: Option<String>,
584    public: bool,
585  ) -> Result<T, ErrorResponse> {
586    self.map_err(|e| {
587      let mut new_error = ErrorResponse {
588        status_code,
589        error_text: e.to_string(),
590        original_text: None,
591        public_error: public,
592      };
593      if error_text_replacement.is_some() {
594        new_error.original_text = Some(new_error.error_text.to_owned());
595        new_error.error_text = error_text_replacement.unwrap();
596      }
597      new_error
598    })
599  }
600}
601
602#[cfg(feature = "salvo")]
603#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
604impl<U> From<std::sync::mpsc::SendError<U>> for ErrorResponse {
605  /// Создаёт `ErrorResponse` из данной ошибки.
606  fn from(value: std::sync::mpsc::SendError<U>) -> Self {
607    value.to_string().into()
608  }
609}
610
611#[cfg(feature = "salvo")]
612#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
613impl_consider!(salvo::http::header::ToStrError);
614
615#[cfg(feature = "bb8-redis")]
616#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
617impl_consider!(bb8_redis::redis::RedisError);
618
619#[cfg(feature = "bb8-redis")]
620#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
621impl_consider!(bb8::RunError<bb8_redis::redis::RedisError>);
622
623#[cfg(feature = "bb8-mongo")]
624#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
625impl_consider!(bb8_mongodb::Error);
626
627#[cfg(feature = "bb8-mongo")]
628#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
629impl_consider!(mongodb::error::Error);
630
631#[cfg(feature = "dotenv")]
632#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
633impl_consider!(dotenv::Error);
634
635#[cfg(feature = "log4rs")]
636#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
637impl_consider!(log4rs::config::runtime::ConfigErrors);
638
639#[cfg(feature = "sea-orm")]
640#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
641impl_consider!(sea_orm::DbErr);
642
643#[cfg(feature = "serde-yaml")]
644#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
645impl_consider!(serde_yaml::Error);
646
647#[cfg(feature = "reqwest")]
648#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
649impl_consider!(reqwest::Error);
650
651#[cfg(feature = "base64")]
652#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
653impl_consider!(base64::DecodeError);
654
655#[cfg(feature = "uuid")]
656#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
657impl_consider!(uuid::Error);
658
659#[cfg(feature = "sqlx")]
660#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
661impl_consider!(sqlx::Error);
662
663#[cfg(feature = "salvo")]
664#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
665impl_consider!(salvo::http::errors::StatusError);
666
667#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
668impl_consider_cli!(rmp_serde::encode::Error);
669#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
670impl_consider_cli!(rmp_serde::decode::Error);
671#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
672impl_consider_cli!(std::io::Error);
673#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
674impl_consider_cli!(std::string::FromUtf8Error);
675#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
676impl_consider_cli!(log::SetLoggerError);
677#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
678impl_consider_cli!(serde_json::Error);
679#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
680impl_consider_cli!(BoxDynError);
681
682#[cfg(feature = "reqwest")]
683#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
684impl_consider_cli!(reqwest::Error);
685
686#[cfg(feature = "base64")]
687#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
688impl_consider_cli!(base64::DecodeError);
689
690#[cfg(feature = "uuid")]
691#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
692impl_consider_cli!(uuid::Error);
693
694#[cfg(feature = "web-sys")]
695#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
696impl<T> ConsiderCli<T> for Result<T, web_sys::wasm_bindgen::JsValue> {
697  /// Изменяет параметры возможной ошибки на указанные.
698  fn consider_cli(self, error_text_replacement: Option<String>) -> Result<T, CliError> {
699    self.map_err(|e| {
700      let mut new_error = CliError {
701        message: e.as_string().unwrap_or_default(),
702      };
703      if error_text_replacement.is_some() {
704        new_error.message = error_text_replacement.unwrap();
705      }
706      new_error
707    })
708  }
709}
710
711#[cfg(feature = "web-sys")]
712#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
713impl From<web_sys::wasm_bindgen::JsValue> for CliError {
714  /// Создаёт `CliError` из данной ошибки.
715  fn from(value: web_sys::wasm_bindgen::JsValue) -> Self {
716    let s = value.as_string().unwrap_or_default();
717    format!("Ошибка в JavaScript: {}", s).into()
718  }
719}
720
721#[cfg(feature = "web-ws")]
722#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
723impl_consider_cli!(ws_stream_wasm::WsErr);