diff --git a/src/api_result.rs b/src/api_result.rs index c9e3bdb..065f01f 100644 --- a/src/api_result.rs +++ b/src/api_result.rs @@ -4,7 +4,6 @@ use kxio::net::Response; use crate::{e, s}; pub struct APIResult { - pub text: String, pub result: Result, } @@ -21,10 +20,9 @@ impl serde::Deserialize<'a>> APIResult { e }) .map_err(kxio::net::Error::from); - Self { text, result } + Self { result } } Err(e) => Self { - text: s!(""), result: Err(e), }, } diff --git a/src/config.rs b/src/config.rs index 11f874d..224bb46 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,13 +1,35 @@ // use color_eyre::Result; -use crate::{f, s, Ctx, NAME}; +use crate::{ + f, + nextcloud::model::{NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudUsername}, + s, Ctx, NAME, +}; #[derive(Clone, Debug, derive_more::From, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)] -pub struct TrelloConfig { - pub api_key: String, - pub api_secret: String, - pub board_name: String, +pub(crate) struct TrelloConfig { + pub(crate) api_key: String, + pub(crate) api_secret: String, + pub(crate) board_name: String, +} + +#[derive( + Clone, + Debug, + derive_more::From, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Deserialize, + derive_more::Constructor, +)] +pub(crate) struct NextcloudConfig { + pub(crate) hostname: NextcloudHostname, + pub(crate) username: NextcloudUsername, + pub(crate) password: NextcloudPassword, + pub(crate) board_id: NextcloudBoardId, } #[derive( @@ -22,7 +44,8 @@ pub struct TrelloConfig { serde::Deserialize, )] pub struct AppConfig { - pub trello: TrelloConfig, + pub(crate) trello: TrelloConfig, + pub(crate) nextcloud: NextcloudConfig, } impl AppConfig { pub fn load(ctx: &Ctx) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 31a267a..093234f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,14 @@ use std::path::PathBuf; use clap::Parser; +pub use config::AppConfig; use kxio::{fs::FileSystem, net::Net}; -// mod api_result; +mod api_result; mod config; mod init; mod macros; +pub mod nextcloud; mod template; // mod trello; diff --git a/src/nextcloud/board.rs b/src/nextcloud/board.rs new file mode 100644 index 0000000..de3db0b --- /dev/null +++ b/src/nextcloud/board.rs @@ -0,0 +1,22 @@ +// +use kxio::net::Net; + +use crate::{p, AppConfig, Ctx}; +use crate::{p, FullCtx}; + +use super::DeckClient; + +pub async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> { + let dc = DeckClient::new(&ctx.cfg.nextcloud, ctx.net); + let apiresult = dc.get_boards().await; + if dump { + p!("{}", apiresult.text); + } else { + let mut boards = apiresult.result?; + boards.sort_by_key(|stack| stack.title.clone()); + boards + .iter() + .for_each(|stack| p!("{}:{}", stack.id, stack.title)); + } + Ok(()) + } diff --git a/src/nextcloud/mod.rs b/src/nextcloud/mod.rs new file mode 100644 index 0000000..df3af82 --- /dev/null +++ b/src/nextcloud/mod.rs @@ -0,0 +1,113 @@ +// +use kxio::net::Net; + +use crate::api_result::APIResult; +use crate::{config::NextcloudConfig, f}; + +use model::{Board, Card, NextcloudBoardId, Stack}; + +pub mod model; + +#[cfg(test)] +mod tests; + +pub(crate) struct DeckClient { + net: Net, + hostname: String, + username: String, + password: String, +} + +impl DeckClient { + pub fn new(cfg: &NextcloudConfig, net: Net) -> Self { + Self { + net, + hostname: cfg.hostname.to_string(), + username: cfg.username.to_string(), + password: cfg.password.to_string(), + } + } + + fn url(&self, path: impl Into) -> String { + f!( + "https://{}/index.php/apps/deck/api/v1.0/{}", + self.hostname, + path.into() + ) + } + + pub async fn get_boards(&self) -> APIResult> { + APIResult::new( + self.net + .get(self.url("boards")) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .send() + .await, + ) + .await + } + + pub async fn create_board(&self, title: &str, color: &str) -> APIResult { + APIResult::new( + self.net + .post(self.url("boards")) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .body( + serde_json::json!({ + "title": title, + "color": color + }) + .to_string(), + ) + .send() + .await, + ) + .await + } + + pub async fn get_stacks(&self, board_id: NextcloudBoardId) -> APIResult> { + APIResult::new( + self.net + .get(self.url(f!("boards/{board_id}/stacks"))) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .send() + .await, + ) + .await + } + + pub async fn create_card( + &self, + board_id: i64, + stack_id: i64, + title: &str, + description: Option<&str>, + ) -> APIResult { + let url = format!( + "https://{}/index.php/apps/deck/api/v1.0/boards/{}/stacks/{}/cards", + self.hostname, board_id, stack_id + ); + + let mut json = serde_json::json!({ + "title": title, + }); + + if let Some(desc) = description { + json["description"] = serde_json::Value::String(desc.to_string()); + } + + APIResult::new( + self.net + .post(&url) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .body(json.to_string()) + .send() + .await, + ) + .await + } +} diff --git a/src/nextcloud/model.rs b/src/nextcloud/model.rs new file mode 100644 index 0000000..94fc87f --- /dev/null +++ b/src/nextcloud/model.rs @@ -0,0 +1,168 @@ +use derive_more::derive::Display; +// +use serde::{Deserialize, Serialize}; + +use crate::newtype; + +newtype!( + NextcloudHostname, + String, + Display, + PartialOrd, + Ord, + "Hostname of the Nextcloud server" +); +newtype!( + NextcloudUsername, + String, + Display, + PartialOrd, + Ord, + "Username to authenticate as" +); +newtype!( + NextcloudPassword, + String, + PartialOrd, + Ord, + "Password to authenticate with" +); + +newtype!( + NextcloudBoardId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Board" +); +newtype!( + NextcloudStackId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Stack" +); +newtype!( + NextcloudCardId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Card" +); +newtype!( + NextcloudLabelId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Label" +); +newtype!( + NextcloudOrder, + i64, + Copy, + Display, + PartialOrd, + Ord, + "Relative position of the item amongst its peers" +); +newtype!( + NextcloudETag, + String, + PartialOrd, + Ord, + "ETag for a resource" +); +newtype!( + NextcloudBoardTitle, + String, + PartialOrd, + Ord, + "Title of the Board" +); +newtype!( + NextcloudBoardOwner, + String, + PartialOrd, + Ord, + "Owner of the Board" +); +newtype!( + NextcloudBoardColour, + String, + PartialOrd, + Ord, + "Colour of the Board" +); + +newtype!( + NextcloudStackTitle, + String, + PartialOrd, + Ord, + "Title of the Stack" +); +newtype!( + NextcloudCardTitle, + String, + PartialOrd, + Ord, + "Title of the Card" +); + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct Board { + pub id: NextcloudBoardId, + pub title: NextcloudBoardTitle, + pub owner: NextcloudBoardOwner, + pub color: NextcloudBoardColour, + pub archived: bool, + pub labels: Vec