// zotapi - Rust wrapper for Zot API as implemented by Hubzilla
// Copyright (C) 2023 Harald Eilertsen <haraldei@anduin.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::{
Channel,
Error,
};
use url::Url;
use reqwest::{
self,
header::{ACCEPT},
};
use serde::Serialize;
#[derive(Debug)]
pub struct ZotApi {
client: reqwest::Client,
base_url: Url,
channel: String,
pw: String,
}
pub fn new(url: &str, channel: &str, pw: &str) -> ZotApi {
ZotApi {
client: reqwest::Client::new(),
base_url: Url::parse(url).unwrap().join("api/z/1.0/").unwrap(),
channel: String::from(channel),
pw: String::from(pw),
}
}
impl ZotApi {
/**
* Returns the channel object of the logged in channel.
*/
pub async fn verify(&self) -> Result<Channel, Box<dyn std::error::Error>> {
let raw = self.get("verify").send().await?.text().await?;
let mut channel: Channel = serde_json::from_str(&raw)?;
channel.xchan = serde_json::from_str(&raw)?;
Ok(channel)
}
/**
* Returns the version of the server.
*
* This API can be invoked without authentication.
*/
pub async fn version(&self) -> Result<String, Box<dyn std::error::Error>> {
Ok(self.get("version").send().await?.text().await?)
}
/**
* Returns the list of channels for this account.
*/
pub async fn channel_list(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let json = self.get("channel/list")
.send().await?
.text().await?;
Ok(serde_json::from_str(&json)?)
}
/**
* Return the channel stream as json.
*/
pub async fn channel_stream(&self) -> Result<String, Error> {
let response = self.get("channel/stream").send().await?;
if response.status().is_success() {
Ok(response.text().await?)
} else {
Err(response.status().into())
}
}
/**
* Return all data from channel for export.
*/
pub async fn channel_export(&self) -> Result<String, Box<dyn std::error::Error>> {
Ok(self.get("channel/export/basic").send().await?.text().await?)
}
/// Return a RequestBuilder object that's set up with the correct
/// path and headers for performing a zot api request.
pub fn get(&self, path: &str) -> reqwest::RequestBuilder {
self.client.get(&self.url(path, &()))
.header(ACCEPT, "application/json")
.basic_auth(self.channel.clone(), Some(self.pw.clone()))
}
fn url<T>(&self, path: &str, args: &T) -> String
where
T: Serialize + std::fmt::Debug,
{
let mut r = self.base_url.clone().join(path).unwrap();
if let Ok(a) = serde_qs::to_string(dbg!(args)) {
r.set_query(Some(&a));
}
r.to_string()
}
}