diff --git a/Cargo.lock b/Cargo.lock index c05560c..5634e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "byteorder" version = "1.4.3" @@ -110,10 +116,12 @@ version = "0.1.0" dependencies = [ "clap", "gitlab", + "iso8601", "log", "reqwest", "serde", "simple-home-dir", + "tabled", "tokio", "toml", "url", @@ -750,6 +758,16 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", + "serde", +] + [[package]] name = "itertools" version = "0.10.5" @@ -959,6 +977,17 @@ version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +[[package]] +name = "papergrid" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -983,6 +1012,30 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -1335,6 +1388,30 @@ dependencies = [ "libc", ] +[[package]] +name = "tabled" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e" +dependencies = [ + "papergrid", + "tabled_derive", + "unicode-width", +] + +[[package]] +name = "tabled_derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c138f99377e5d653a371cdad263615634cfc8467685dfe8e73e2b8e98f44b17" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tempfile" version = "3.9.0" @@ -1586,6 +1663,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "void" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index 2cd682c..c247c84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ url = "2.3.1" tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros"] } simple-home-dir = "0.2.1" log = "0.4.17" +tabled = "0.15.0" +iso8601 = { version = "0.6.1" , features = ["serde"]} diff --git a/src/cli.rs b/src/cli.rs index 44edd08..1b4fed9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -21,6 +21,7 @@ pub(crate) enum Commands { #[derive(Subcommand)] pub(crate) enum Planning { I2work(Issue2Work), + Load, } #[derive(Args, Debug)] diff --git a/src/main.rs b/src/main.rs index 19789f7..351f91c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,7 @@ async fn main() { let result = match cli.command { Some(Planning(I2work(args))) => planning::issue2work::issue2work(config, &args).await, + Some(Planning(cli::Planning::Load)) => planning::planning_load::planning_load(config).await, Some(Test) => debug::debug(config).await, None => Err(GeneralError { description: "No command launched".to_string(), diff --git a/src/openproject/client.rs b/src/openproject/client.rs index 0cfb407..abd20d9 100644 --- a/src/openproject/client.rs +++ b/src/openproject/client.rs @@ -6,8 +6,8 @@ use serde::Deserialize; use std::error::Error; #[derive(Deserialize, Debug)] -pub(crate) struct OpenProjectError { - pub(crate) description: String, +pub struct OpenProjectError { + pub description: String, } impl From for OpenProjectError { diff --git a/src/openproject/work.rs b/src/openproject/work.rs index d9aca46..2f2bc28 100644 --- a/src/openproject/work.rs +++ b/src/openproject/work.rs @@ -1,3 +1,6 @@ +use crate::openproject::client::{handle_response_status, Client, OpenProjectError}; +use crate::openproject::hal::Link; +use iso8601::Duration; use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] @@ -19,8 +22,119 @@ pub struct DescriptionWriter { pub(crate) raw: String, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Clone)] pub struct WorkPackage { pub id: u64, pub subject: String, + #[serde(alias = "estimatedTime")] + pub estimated_time: Option, + #[serde(alias = "spentTime")] + pub spent_time: Option, +} + +#[derive(Deserialize, Debug)] +pub struct WorkPackageCollectionLinks { + #[serde(alias = "nextByOffset")] + pub next_by_offset: Option, +} + +#[derive(Deserialize, Debug)] +pub struct WorkPackagesElements { + pub elements: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct WorkPackageCollection { + pub total: u64, + pub count: u64, + pub offset: u64, + #[serde(alias = "pageSize")] + pub page_size: u64, + #[serde(alias = "_links")] + pub links: WorkPackageCollectionLinks, + #[serde(alias = "_embedded")] + pub embedded: WorkPackagesElements, +} + +impl WorkPackageCollection { + pub fn has_next(&self) -> bool { + self.links.next_by_offset.is_some() + } + + pub fn is_last(&self) -> bool { + !self.has_next() + } +} + +pub trait WorkPackageCollectionClient { + async fn work_package(&self) -> Result, OpenProjectError>; +} + +trait GetWorkPackageCollectionClient { + async fn first_work_packages_collection( + &self, + ) -> Result; + async fn next_work_packages_collection( + &self, + current: &WorkPackageCollection, + ) -> Result; +} + +impl GetWorkPackageCollectionClient for Client { + async fn first_work_packages_collection( + &self, + ) -> Result { + let client = reqwest::Client::new(); + + let response = client + .get(format!("{}/api/v3/work_packages", self.base_url)) + .basic_auth("apikey", Some(&self.token)) + .send() + .await?; + + let collection = handle_response_status(response, "could not find work packages").await?; + + Ok(collection) + } + + async fn next_work_packages_collection( + &self, + current: &WorkPackageCollection, + ) -> Result { + let client = reqwest::Client::new(); + + let response = client + .get(format!( + "{}/{}", + self.base_url, + current.links.next_by_offset.clone().unwrap().href + )) + .basic_auth("apikey", Some(&self.token)) + .send() + .await?; + + let collection = handle_response_status(response, "could not find work packages").await?; + + Ok(collection) + } +} + +impl WorkPackageCollectionClient for Client { + async fn work_package(&self) -> Result, OpenProjectError> { + let mut work_packages: Vec = vec![]; + let mut collection = self.first_work_packages_collection().await?; + + for w in collection.embedded.elements.iter_mut() { + work_packages.push(w.clone()); + } + + while !collection.is_last() { + collection = self.next_work_packages_collection(&collection).await?; + for w in collection.embedded.elements.iter() { + work_packages.push(w.clone()); + } + } + + Ok(work_packages) + } } diff --git a/src/planning/mod.rs b/src/planning/mod.rs index dd9b3f9..0be28d8 100644 --- a/src/planning/mod.rs +++ b/src/planning/mod.rs @@ -1 +1,2 @@ pub(crate) mod issue2work; +pub(crate) mod planning_load; diff --git a/src/planning/planning_load.rs b/src/planning/planning_load.rs new file mode 100644 index 0000000..3cdb5bb --- /dev/null +++ b/src/planning/planning_load.rs @@ -0,0 +1,15 @@ +use crate::config::Config; +use crate::error::GeneralError; +use crate::openproject::client::Client; +use crate::openproject::work::WorkPackageCollectionClient; + +pub async fn planning_load(config: Config) -> Result<(), GeneralError> { + let open_project_client = Client::from_config(&config.openproject); + let work_packages = open_project_client.work_package().await?; + + for w in work_packages { + println!("{:?}", w); + } + + Ok(()) +}