1use async_trait::async_trait;
2use log::{debug, trace};
3use serde::*;
4
5#[cfg(feature = "app-file-cache")]
6pub use file_cached::FileCachedAppProvider;
7
8use crate::error::Error;
9
10#[cfg(feature = "app-file-cache")]
11mod file_cached;
12
13#[derive(Serialize, Deserialize, Clone, Debug)]
15pub struct AppInfo {
16 pub name: String,
18 pub redirect_uri: String,
20}
21
22#[derive(Serialize, Deserialize, Clone, Debug)]
24pub struct RegisteredApp {
25 pub name: String,
27 pub client_id: String,
29 pub client_secret: String,
31}
32
33#[async_trait]
40pub trait AppProvider {
41 async fn get_app_for(&mut self, instance_domain: &str) -> Result<RegisteredApp, Error>;
44}
45
46impl AppInfo {
47 pub fn new(name: &str, redirect_uri: &str) -> Self {
49 Self {
50 name: name.to_owned(),
51 redirect_uri: redirect_uri.to_owned(),
52 }
53 }
54}
55
56pub async fn register_new_app(
59 instance_domain: &str,
60 app: &AppInfo,
61) -> Result<RegisteredApp, Error> {
62 let url = format!("https://{instance_domain}/api/v1/apps");
63 let params = [
64 ("client_name", app.name.as_str()),
65 ("redirect_uris", app.redirect_uri.as_str()),
66 ("scopes", "read:accounts"),
67 ];
68
69 debug!("Registering new app '{}' at `{instance_domain}`", app.name);
70 trace!("Registration url is {url}");
71
72 let client = reqwest::Client::new();
73 let response = client
74 .post(&url)
75 .form(¶ms)
76 .send()
77 .await
78 .map_err(|_| Error::AppRegistrationError("Failed to connect to server"))?;
79
80 trace!("Response status {}", response.status());
81 if response.status() != 200 {
82 return Err(Error::TokenObtainError("Authorization failure"));
83 }
84
85 let registered_app: RegisteredApp = response
86 .json()
87 .await
88 .map_err(|_| Error::AppRegistrationError("Unable to parse response from server"))?;
89
90 debug!(
91 "App registration done, client_id={}",
92 registered_app.client_id
93 );
94
95 Ok(registered_app)
96}