Skip to main content

deepl_rustls/endpoint/
mod.rs

1use serde::{Deserialize, Serialize};
2use std::{future::Future, pin::Pin};
3use thiserror::Error;
4
5pub mod document;
6pub mod glossary;
7pub mod languages;
8pub mod translate;
9pub mod usage;
10
11/// Representing error during interaction with DeepL
12#[derive(Debug, Error)]
13pub enum Error {
14    #[error("invalid response: {0}")]
15    InvalidResponse(String),
16
17    #[error("request fail: {0}")]
18    RequestFail(String),
19
20    #[error("fail to read file {0}: {1}")]
21    ReadFileError(String, tokio::io::Error),
22
23    #[error(
24        "trying to download a document using a non-existing document ID or the wrong document key"
25    )]
26    NonExistDocument,
27
28    #[error("tries to download a translated document that is currently being processed and is not yet ready for download")]
29    TranslationNotDone,
30
31    #[error("fail to write file: {0}")]
32    WriteFileError(String),
33}
34
35const REPO_URL: &'static str = "https://github.com/Avimitin/deepl-rs";
36
37/// Alias Result<T, E> to Result<T, [`Error`]>
38type Result<T, E = Error> = std::result::Result<T, E>;
39
40/// Pollable alias to a Pin<Box<dyn Future<...>>>. A convenient type for impl
41/// [`IntoFuture`](std::future::IntoFuture) trait
42type Pollable<'poll, T> = Pin<Box<dyn Future<Output = T> + Send + Sync + 'poll>>;
43
44/// A self implemented Type Builder
45#[macro_export]
46macro_rules! impl_requester {
47    (
48        $name:ident {
49            @required{
50                $($must_field:ident: $must_type:ty,)+
51            };
52            @optional{
53                $($opt_field:ident: $opt_type:ty,)*
54            };
55        } -> $fut_ret:ty;
56    ) => {
57        use paste::paste;
58        use $crate::{DeepLApi, Error};
59
60        paste! {
61            #[doc = "Builder type for `" $name "`"]
62            pub struct $name<'a> {
63                client: &'a DeepLApi,
64
65                $($must_field: $must_type,)+
66                $($opt_field: Option<$opt_type>,)*
67            }
68
69            impl<'a> $name<'a> {
70                pub fn new(client: &'a DeepLApi, $($must_field: $must_type,)+) -> Self {
71                    Self {
72                        client,
73                        $($must_field,)+
74                        $($opt_field: None,)*
75                    }
76                }
77
78                $(
79                    #[doc = "Setter for `" $opt_field "`"]
80                    pub fn $opt_field(&mut self, $opt_field: $opt_type) -> &mut Self {
81                        self.$opt_field = Some($opt_field);
82                        self
83                    }
84                )*
85            }
86        }
87    };
88}
89
90/// Formality preference for translation
91#[derive(Serialize)]
92#[serde(rename_all = "snake_case")]
93pub enum Formality {
94    Default,
95    More,
96    Less,
97    PreferMore,
98    PreferLess,
99}
100
101impl AsRef<str> for Formality {
102    fn as_ref(&self) -> &str {
103        match self {
104            Self::Default => "default",
105            Self::More => "more",
106            Self::Less => "less",
107            Self::PreferMore => "prefer_more",
108            Self::PreferLess => "prefer_less",
109        }
110    }
111}
112
113impl std::fmt::Display for Formality {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        write!(f, "{}", self.as_ref())
116    }
117}
118
119// detail message of the API error
120#[derive(Deserialize)]
121struct DeepLErrorResp {
122    message: String,
123}
124
125/// Turn DeepL API error message into [`Error`]
126async fn extract_deepl_error<T>(res: reqwest::Response) -> Result<T> {
127    let resp = res
128        .json::<DeepLErrorResp>()
129        .await
130        .map_err(|err| Error::InvalidResponse(format!("invalid error response: {err}")))?;
131    Err(Error::RequestFail(resp.message))
132}