1#![warn(
5 missing_copy_implementations,
6 missing_debug_implementations,
7 missing_docs,
8 non_ascii_idents,
9 trivial_casts,
10 unused,
11 unused_qualifications,
12 clippy::expect_used,
13 clippy::unwrap_used,
14 clippy::panic
15)]
16#![deny(unsafe_code)]
17
18use std::{
21 error,
22 fmt::{self, Display, Formatter},
23 path::PathBuf,
24 result,
25};
26
27#[cfg(feature = "intervals_icu")]
28pub mod intervals_icu;
29#[cfg(feature = "strava")]
30pub mod strava;
31
32pub type Result<T, E = Error> = result::Result<T, E>;
34
35#[derive(Debug)]
40pub struct Error {
41 kind: ErrorKind,
42 error: Option<Box<dyn error::Error + Send + Sync>>,
43}
44
45impl Error {
46 pub fn new<E: Into<Box<dyn error::Error + Send + Sync>>>(kind: ErrorKind, error: E) -> Self {
48 Self {
49 kind,
50 error: Some(error.into()),
51 }
52 }
53
54 pub fn kind(&self) -> &ErrorKind {
56 &self.kind
57 }
58
59 pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
61 self.error
62 }
63}
64
65impl Display for Error {
66 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
67 self.kind.fmt(f)
68 }
69}
70
71impl From<ErrorKind> for Error {
72 fn from(kind: ErrorKind) -> Self {
73 Self { kind, error: None }
74 }
75}
76
77impl error::Error for Error {
78 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
79 if let Some(error) = &self.error {
80 Some(error.as_ref())
81 } else {
82 None
83 }
84 }
85}
86
87#[cfg(feature = "multipart")]
88impl From<multipart::client::lazy::LazyIoError<'_>> for Error {
89 fn from(error: multipart::client::lazy::LazyIoError<'_>) -> Self {
90 Self::new(
91 ErrorKind::MultipartEncodingFailed {
92 field: error.field_name.map(|field| field.into_owned()),
93 },
94 error.error,
95 )
96 }
97}
98
99#[cfg(feature = "ureq")]
100impl From<ureq::Error> for Error {
101 fn from(error: ureq::Error) -> Self {
102 let mut message = error.to_string();
103 if let ureq::Error::Status(_, response) = error {
104 if let Ok(response) = response.into_string() {
105 use std::fmt::Write as _;
106 write!(&mut message, ", message: {}", response).ok();
107 }
108 }
109 Error::new(ErrorKind::RequestFailed, message)
110 }
111}
112
113#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
115pub enum ErrorKind {
116 DataTypeParsingFailed {
118 filename: PathBuf,
120 },
121 FileReadingFailed {
123 path: PathBuf,
125 },
126 MultipartEncodingFailed {
128 field: Option<String>,
130 },
131 RequestFailed,
133 ResponseFailed,
135 UrlParsingFailed {
137 url: String,
139 },
140}
141
142impl Display for ErrorKind {
143 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144 match self {
145 Self::DataTypeParsingFailed { filename } => {
146 write!(
147 f,
148 "failed to parse data type from filename {}",
149 filename.display()
150 )
151 }
152 Self::FileReadingFailed { path } => {
153 write!(f, "failed to read file {}", path.display())
154 }
155 Self::MultipartEncodingFailed { field } => {
156 if let Some(field) = field {
157 write!(f, "failed to encode multipart field {field}")
158 } else {
159 write!(f, "failed to encode multipart data")
160 }
161 }
162 Self::RequestFailed => write!(f, "failed to send API request"),
163 Self::ResponseFailed => write!(f, "failed to read API response"),
164 Self::UrlParsingFailed { url } => write!(f, "failed to parse the URL {}", url),
165 }
166 }
167}