modio/client/
mod.rs

1use std::sync::Arc;
2
3use reqwest::Client;
4
5use crate::auth::{Auth, Credentials, Token};
6use crate::download::{DownloadAction, Downloader};
7use crate::error::Result;
8use crate::games::{GameRef, Games};
9use crate::mods::ModRef;
10use crate::reports::Reports;
11use crate::request::RequestBuilder;
12use crate::routing::Route;
13use crate::types::id::{GameId, ModId};
14use crate::user::Me;
15
16mod builder;
17
18pub use builder::Builder;
19
20const DEFAULT_HOST: &str = "https://api.mod.io/v1";
21const TEST_HOST: &str = "https://api.test.mod.io/v1";
22const DEFAULT_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), '/', env!("CARGO_PKG_VERSION"));
23
24/// Endpoint interface to interacting with the [mod.io](https://mod.io) API.
25#[derive(Clone, Debug)]
26pub struct Modio {
27    pub(crate) inner: Arc<ClientRef>,
28}
29
30#[derive(Debug)]
31pub(crate) struct ClientRef {
32    pub(crate) host: String,
33    pub(crate) client: Client,
34    pub(crate) credentials: Credentials,
35}
36
37impl Modio {
38    /// Constructs a new `Builder` to configure a `Modio` client.
39    ///
40    /// This is the same as `Builder::new(credentials)`.
41    pub fn builder<C: Into<Credentials>>(credentials: C) -> Builder {
42        Builder::new(credentials)
43    }
44
45    /// Create an endpoint to [https://api.mod.io/v1](https://docs.mod.io/restapiref/#mod-io-api-v1).
46    pub fn new<C>(credentials: C) -> Result<Self>
47    where
48        C: Into<Credentials>,
49    {
50        Builder::new(credentials).build()
51    }
52
53    /// Create an endpoint to a different host.
54    pub fn host<H, C>(host: H, credentials: C) -> Result<Self>
55    where
56        H: Into<String>,
57        C: Into<Credentials>,
58    {
59        Builder::new(credentials).host(host).build()
60    }
61
62    /// Return an endpoint with new credentials.
63    #[must_use]
64    pub fn with_credentials<CR>(&self, credentials: CR) -> Self
65    where
66        CR: Into<Credentials>,
67    {
68        Self {
69            inner: Arc::new(ClientRef {
70                host: self.inner.host.clone(),
71                client: self.inner.client.clone(),
72                credentials: credentials.into(),
73            }),
74        }
75    }
76
77    /// Return an endpoint with a new token.
78    #[must_use]
79    pub fn with_token<T>(&self, token: T) -> Self
80    where
81        T: Into<Token>,
82    {
83        Self {
84            inner: Arc::new(ClientRef {
85                host: self.inner.host.clone(),
86                client: self.inner.client.clone(),
87                credentials: Credentials {
88                    api_key: self.inner.credentials.api_key.clone(),
89                    token: Some(token.into()),
90                },
91            }),
92        }
93    }
94
95    /// Return a reference to an interface for requesting access tokens.
96    pub fn auth(&self) -> Auth {
97        Auth::new(self.clone())
98    }
99
100    /// Return a reference to an interface that provides access to game information.
101    pub fn games(&self) -> Games {
102        Games::new(self.clone())
103    }
104
105    /// Return a reference to a game.
106    pub fn game(&self, game_id: GameId) -> GameRef {
107        GameRef::new(self.clone(), game_id)
108    }
109
110    /// Return a reference to a mod.
111    pub fn mod_(&self, game_id: GameId, mod_id: ModId) -> ModRef {
112        ModRef::new(self.clone(), game_id, mod_id)
113    }
114
115    /// Returns [`Downloader`] for saving to file or retrieving
116    /// the data via [`Stream`].
117    ///
118    /// The download fails with [`modio::download::Error`] as source
119    /// if a primary file, a specific file or a specific version is not found.
120    ///
121    /// [`Downloader`]: crate::download::Downloader
122    /// [`modio::download::Error`]: crate::download::Error
123    /// [`Stream`]: futures_util::Stream
124    ///
125    /// # Example
126    /// ```no_run
127    /// use futures_util::{future, TryStreamExt};
128    /// use modio::download::{DownloadAction, ResolvePolicy};
129    /// use modio::types::id::Id;
130    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
131    /// #    let modio = modio::Modio::new("user-or-game-api-key")?;
132    ///
133    /// // Download the primary file of a mod.
134    /// let action = DownloadAction::Primary {
135    ///     game_id: Id::new(5),
136    ///     mod_id: Id::new(19),
137    /// };
138    /// modio
139    ///     .download(action)
140    ///     .await?
141    ///     .save_to_file("mod.zip")
142    ///     .await?;
143    ///
144    /// // Download the specific file of a mod.
145    /// let action = DownloadAction::File {
146    ///     game_id: Id::new(5),
147    ///     mod_id: Id::new(19),
148    ///     file_id: Id::new(101),
149    /// };
150    /// modio
151    ///     .download(action)
152    ///     .await?
153    ///     .save_to_file("mod.zip")
154    ///     .await?;
155    ///
156    /// // Download the specific version of a mod.
157    /// // if multiple files are found then the latest file is downloaded.
158    /// // Set policy to `ResolvePolicy::Fail` to return with
159    /// // `modio::download::Error::MultipleFilesFound` as source error.
160    /// let action = DownloadAction::Version {
161    ///     game_id: Id::new(5),
162    ///     mod_id: Id::new(19),
163    ///     version: "0.1".to_string(),
164    ///     policy: ResolvePolicy::Latest,
165    /// };
166    /// modio
167    ///     .download(action)
168    ///     .await?
169    ///     .stream()
170    ///     .try_for_each(|bytes| {
171    ///         println!("Bytes: {:?}", bytes);
172    ///         future::ok(())
173    ///     })
174    ///     .await?;
175    /// #    Ok(())
176    /// # }
177    /// ```
178    pub async fn download<A>(&self, action: A) -> Result<Downloader>
179    where
180        DownloadAction: From<A>,
181    {
182        Downloader::new(self.clone(), action.into()).await
183    }
184
185    /// Return a reference to an interface that provides access to resources owned by the user
186    /// associated with the current authentication credentials.
187    pub fn user(&self) -> Me {
188        Me::new(self.clone())
189    }
190
191    /// Return a reference to an interface to report games, mods and users.
192    pub fn reports(&self) -> Reports {
193        Reports::new(self.clone())
194    }
195
196    pub(crate) fn request(&self, route: Route) -> RequestBuilder {
197        RequestBuilder::new(self.clone(), route)
198    }
199}