diff --git a/src/conversion.rs b/src/conversion.rs index e45d16a..3b04bb5 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -1,6 +1,12 @@ // -use crate::nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle}; -use crate::trello::model::card::{TrelloCardDescription, TrelloCardName}; +use crate::nextcloud::model::NextcloudStackTitle; +use crate::{ + nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle, NextcloudOrder}, + trello::model::{ + card::{TrelloCardDescription, TrelloCardName, TrelloCardPosition}, + list::{TrelloListName, TrelloListPosition}, + }, +}; impl From<&TrelloCardName> for NextcloudCardTitle { fn from(value: &TrelloCardName) -> Self { @@ -13,3 +19,21 @@ impl From<&TrelloCardDescription> for NextcloudCardDescription { Self::new(value.to_string()) } } + +impl From<&TrelloListName> for NextcloudStackTitle { + fn from(value: &TrelloListName) -> Self { + Self::new(value.to_string()) + } +} + +impl From<&TrelloListPosition> for NextcloudOrder { + fn from(value: &TrelloListPosition) -> Self { + Self::new(value.0) + } +} + +impl From<&TrelloCardPosition> for NextcloudOrder { + fn from(value: &TrelloCardPosition) -> Self { + Self::new(value.0) + } +} diff --git a/src/import/mod.rs b/src/import/mod.rs index bec9c0c..59b29a3 100644 --- a/src/import/mod.rs +++ b/src/import/mod.rs @@ -40,7 +40,9 @@ pub(crate) async fn run(ctx: &FullCtx) -> color_eyre::Result<()> { .map(TrelloBoardId::new) .expect("find selected board"); // get list of trello stacks for the selected board - let trello_stacks = trello_client.board(&trello_board_id).await.result?.lists; + let mut trello_stacks = trello_client.board(&trello_board_id).await.result?.lists; + // sort stacks by position + trello_stacks.sort_by_key(|s| s.pos); // prompt user to select some stacks let trello_stack_names = trello_stacks .iter() diff --git a/src/import/stack.rs b/src/import/stack.rs index 14daf47..553790c 100644 --- a/src/import/stack.rs +++ b/src/import/stack.rs @@ -56,10 +56,12 @@ impl Actor for ImportStackActor { crate::p!(this.ctx.prt, "Importing stack: {}", this.trello_stack.name); // - get the list of trello cards in the stack - let trello_cards = trello_client + let mut trello_cards = trello_client .list_cards(&TrelloListId::new(this.trello_stack.id.as_ref())) .await .result?; + // sort cards by their position + trello_cards.sort_by_key(|card| card.pos); // - for each card in the trello stack for trello_card in trello_cards.into_iter() { diff --git a/src/import/stacks.rs b/src/import/stacks.rs index 79b53d0..3b949c6 100644 --- a/src/import/stacks.rs +++ b/src/import/stacks.rs @@ -21,26 +21,41 @@ use crate::{ async fn create_any_missing_stacks( ctx: &FullCtx, - selected_trello_stack_names: &[String], + selected_trello_stacks: &[TrelloList], nextcloud_board_id: NextcloudBoardId, nextcloud_stacks: Vec, ) -> color_eyre::Result<()> { let deck_client: DeckClient = ctx.deck_client(); // identify any stacks by name from those selected in trello that are missing in nextcloud - let missing_stack_names = selected_trello_stack_names + let missing_stacks = selected_trello_stacks .iter() - .filter(|s| !nextcloud_stacks.iter().any(|ns| ns.title.deref() == *s)) + .filter(|trello_stack| { + !nextcloud_stacks + .iter() + .any(|nextcloud_stack| nextcloud_stack.title.deref() == trello_stack.name.deref()) + }) .cloned() .collect::>(); - if !missing_stack_names.is_empty() { - crate::p!(ctx.prt, "Missing stacks: {:?}", missing_stack_names); + if !missing_stacks.is_empty() { + crate::p!( + ctx.prt, + "Missing stacks: {:?}", + missing_stacks + .iter() + .map(|s| s.name.as_ref()) + .collect::>() + ); // create any missing stacks in nextcloud // for each missing stack - for missing_stack_name in missing_stack_names.into_iter() { + for missing_stack in missing_stacks.into_iter() { // - create the stack let stack = deck_client - .create_stack(nextcloud_board_id, &missing_stack_name.clone().into()) + .create_stack( + nextcloud_board_id, + (&missing_stack.name).into(), + (&missing_stack.pos).into(), + ) .await .result?; p!(ctx.prt, "Created stack: {}", stack.title); @@ -83,7 +98,12 @@ impl Actor for ImportStacksActor { on_actor_start!(this, actor_ref, { // spawn a new ImportStack actor for each trello_stack named in selected_trello_stack_names - + let mut trello_stacks = vec![]; + std::mem::swap(&mut this.trello_stacks, &mut trello_stacks); + let selected_trello_stacks = trello_stacks + .into_iter() + .filter(|s| this.selected_trello_stack_names.contains(s.name.as_ref())) + .collect::>(); // get list of nextcloud stacks in the selected board let nextcloud_stacks = this .ctx @@ -93,7 +113,7 @@ impl Actor for ImportStacksActor { .result?; create_any_missing_stacks( &this.ctx, - &this.selected_trello_stack_names, + &selected_trello_stacks, this.nextcloud_board_id, nextcloud_stacks, ) @@ -107,11 +127,7 @@ impl Actor for ImportStacksActor { .result?; // for each selected trello stack - for selected_trello_stack in this - .trello_stacks - .iter() - .filter(|s| this.selected_trello_stack_names.contains(s.name.as_ref())) - { + for selected_trello_stack in selected_trello_stacks { let nextcloud_stack_id = //this. nextcloud_stacks .iter() diff --git a/src/nextcloud/client.rs b/src/nextcloud/client.rs index 01ca09d..d472e5b 100644 --- a/src/nextcloud/client.rs +++ b/src/nextcloud/client.rs @@ -7,6 +7,7 @@ use kxio::{ use reqwest::multipart; use serde_json::json; +use crate::nextcloud::model::NextcloudOrder; use crate::{ api_result::APIResult, f, @@ -118,13 +119,14 @@ impl<'ctx> DeckClient<'ctx> { pub(crate) async fn create_stack( &self, board_id: NextcloudBoardId, - stack_title: &NextcloudStackTitle, + stack_title: NextcloudStackTitle, + stack_order: NextcloudOrder, ) -> APIResult { self.request_with_body( f!("boards/{board_id}/stacks"), json!({ "title": stack_title, - "order": 999, + "order": stack_order, }) .to_string(), |net, url| net.post(url), diff --git a/src/nextcloud/stack.rs b/src/nextcloud/stack.rs index 72e15f7..b781c45 100644 --- a/src/nextcloud/stack.rs +++ b/src/nextcloud/stack.rs @@ -2,6 +2,7 @@ use clap::Parser; use crate::execute::Execute; +use crate::nextcloud::model::NextcloudOrder; use crate::{p, FullCtx}; #[derive(Parser, Debug)] @@ -50,7 +51,11 @@ impl Execute for NextcloudStackCommand { } => { let api_result = ctx .deck_client() - .create_stack((*board_id).into(), &stack_title.clone().into()) + .create_stack( + (*board_id).into(), + stack_title.clone().into(), + NextcloudOrder::new(999), + ) .await; if *dump { p!(ctx.prt, "{}", api_result.text); diff --git a/src/trello/model/card.rs b/src/trello/model/card.rs index b58b2c0..41502b9 100644 --- a/src/trello/model/card.rs +++ b/src/trello/model/card.rs @@ -11,8 +11,8 @@ newtype!(TrelloCardName, String, Display, "Card Name"); newtype!(TrelloCardDescription, String, Display, "Card Description"); newtype!(TrelloCardDue, String, Display, "Card Due"); -#[derive(Debug, PartialEq, Eq, Display)] -pub struct TrelloCardPosition(i64); +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Display)] +pub(crate) struct TrelloCardPosition(pub i64); impl<'de> Deserialize<'de> for TrelloCardPosition { fn deserialize(deserializer: D) -> Result diff --git a/src/trello/model/list.rs b/src/trello/model/list.rs index 0946fc4..d99023c 100644 --- a/src/trello/model/list.rs +++ b/src/trello/model/list.rs @@ -1,6 +1,6 @@ // use derive_more::derive::Display; -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; use crate::newtype; @@ -14,8 +14,23 @@ newtype!( "List Name" ); +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Display)] +pub(crate) struct TrelloListPosition(pub i64); + +impl<'de> Deserialize<'de> for TrelloListPosition { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // First deserialize as f64 + let value = f64::deserialize(deserializer)?; + // Convert to i64 by rounding + Ok(TrelloListPosition(value.round() as i64)) + } +} #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] pub(crate) struct TrelloList { pub(crate) id: TrelloListId, pub(crate) name: TrelloListName, + pub(crate) pos: TrelloListPosition, }