syncthing-async 0.2.0

A Rust library for controlling syncthing file synchronization through its REST interface
Documentation
use crate::Fallible;
use crate::event_stream::EventStream;
use http::Method;
use http::header::HeaderValue;
use http::uri::Authority;
use reqwest::Client as HttpClient;
use serde::Serialize;
use serde::de::DeserializeOwned as Deserialize;
use std::collections::HashMap;
use syncthing_types::events::{Event, EventType};
use syncthing_types::utils::construct_uri;
use syncthing_types::{API_DEFAULT_AUTHORITY, Timestamp};
use syncthing_types::{API_HEADER_KEY, routes::*};
use syncthing_types::{EMPTY_EVENT_SUBSCRIPTION, system};
use syncthing_types::{cluster, utils};

pub struct Client {
    client: HttpClient,
    authority: Authority,
    api_key: String,
}

impl Client {
    pub fn new(api_key: impl Into<String>) -> Self {
        Self {
            client: HttpClient::new(),
            authority: Authority::from_static(API_DEFAULT_AUTHORITY),
            api_key: api_key.into(),
        }
    }

    pub fn with_authority(api_key: impl Into<String>, authority: Authority) -> Self {
        Self {
            client: HttpClient::new(),
            authority,
            api_key: api_key.into(),
        }
    }

    pub(crate) async fn get<D: Deserialize, T: AsRef<[u8]> + 'static>(
        &self,
        path_and_query: T,
    ) -> Fallible<D> {
        let url = construct_uri(&self.authority, path_and_query)?.to_string();
        let resp = self
            .client
            .request(Method::GET, url)
            .header(API_HEADER_KEY, HeaderValue::from_str(&self.api_key)?)
            .send()
            .await?
            .error_for_status()?;
        Ok(resp.json().await?)
    }

    pub(crate) async fn post<S: Serialize, T: AsRef<[u8]> + 'static>(
        &self,
        path_and_query: T,
        body: &S,
    ) -> Fallible<()> {
        let url = construct_uri(&self.authority, path_and_query)?.to_string();
        self.client
            .request(Method::POST, url)
            .body(serde_json::to_string(body)?)
            .header(API_HEADER_KEY, HeaderValue::from_str(&self.api_key)?)
            .send()
            .await?
            .error_for_status()?;
        Ok(())
    }

    pub async fn get_all_events(
        &self,
        since: Option<u64>,
        limit: Option<u64>,
    ) -> Fallible<Vec<Event>> {
        self.get_events(since, limit, &EMPTY_EVENT_SUBSCRIPTION)
            .await
    }

    pub async fn get_events(
        &self,
        since: Option<u64>,
        limit: Option<u64>,
        events: impl AsRef<[EventType]>,
    ) -> Fallible<Vec<Event>> {
        let path_and_query = utils::construct_event_path_and_query(since, limit, events)?;
        self.get(path_and_query).await
    }

    pub fn subscribe_to(self, events: impl Into<Vec<EventType>>) -> EventStream {
        EventStream::new(self, events.into())
    }

    pub fn subscribe_to_all(self) -> EventStream {
        EventStream::new(self, EMPTY_EVENT_SUBSCRIPTION.clone())
    }

    pub async fn browse(&self, pattern: Option<String>) -> Fallible<Vec<String>> {
        if let Some(pattern) = pattern {
            self.get(format!("{}?current={}", SYSTEM_BROWSE, pattern))
                .await
        } else {
            self.get(SYSTEM_BROWSE).await
        }
    }

    pub async fn get_connections(&self) -> Fallible<system::Connections> {
        self.get(SYSTEM_CONNECTIONS).await
    }

    pub async fn get_discovery_cache(&self) -> Fallible<system::Discovery> {
        self.get(SYSTEM_DISCOVERY).await
    }

    pub async fn get_log(&self, since: Option<Timestamp>) -> Fallible<system::Log> {
        if let Some(since) = since {
            self.get(format!("{}?since={}", SYSTEM_LOG, since.to_rfc3339()))
                .await
        } else {
            self.get(SYSTEM_LOG).await
        }
    }

    pub async fn get_errors(&self) -> Fallible<system::Error> {
        self.get(SYSTEM_ERROR).await
    }

    pub async fn clear_errors(&self) -> Fallible<()> {
        self.post(SYSTEM_ERROR_CLEAR, &()).await
    }

    pub async fn get_loglevels_info(&self) -> Fallible<system::LogLevelsInfo> {
        self.get(SYSTEM_LOGLEVELS).await
    }

    pub async fn get_paths(&self) -> Fallible<HashMap<String, String>> {
        self.get(SYSTEM_PATHS).await
    }

    pub async fn ping(&self) -> Fallible<system::Ping> {
        self.get(SYSTEM_PING).await
    }

    pub async fn status(&self) -> Fallible<system::Status> {
        self.get(SYSTEM_STATUS).await
    }

    pub async fn get_upgrade_info(&self) -> Fallible<system::UpgradeInfo> {
        self.get(SYSTEM_UPGRADE).await
    }

    pub async fn get_version_info(&self) -> Fallible<system::VersionInfo> {
        self.get(SYSTEM_VERSION).await
    }

    pub async fn get_cluster_pending_devices(&self) -> Fallible<cluster::PendingDevices> {
        self.get(CLUSTER_PENDING_DEVICES).await
    }
}