feat: split into actors
This commit is contained in:
parent
61323484a2
commit
0e123898db
15 changed files with 1070 additions and 275 deletions
|
@ -15,6 +15,7 @@ derive_more = { version = "1.0", features = [
|
|||
"from",
|
||||
] }
|
||||
inquire = "0.7"
|
||||
kameo = "0.13"
|
||||
# kxio = {path = "../kxio/"}
|
||||
kxio = "4.0"
|
||||
reqwest = { version = "0.12" , features = ["multipart", "stream"]}
|
||||
|
|
|
@ -1,56 +1,123 @@
|
|||
//
|
||||
use std::path::PathBuf;
|
||||
|
||||
use kameo::{mailbox::unbounded::UnboundedMailbox, Actor};
|
||||
|
||||
use crate::{
|
||||
nextcloud::{
|
||||
client::DeckClient,
|
||||
model::{Card, NextcloudBoardId, NextcloudStackId},
|
||||
},
|
||||
p,
|
||||
trello::{
|
||||
client::TrelloClient,
|
||||
model::{
|
||||
nextcloud::model::{NextcloudBoardId, NextcloudCardId, NextcloudStackId},
|
||||
on_actor_start, p,
|
||||
trello::model::{
|
||||
attachment::{TrelloAttachment, TrelloAttachmentId},
|
||||
card::TrelloShortCard,
|
||||
},
|
||||
card::TrelloCardId,
|
||||
},
|
||||
FullCtx,
|
||||
};
|
||||
|
||||
pub(super) async fn import_attachment(
|
||||
ctx: &FullCtx,
|
||||
// pub(super) async fn import_attachment(
|
||||
// ctx: &FullCtx,
|
||||
// nextcloud_board_id: NextcloudBoardId,
|
||||
// nextcloud_stack_id: NextcloudStackId,
|
||||
// trello_card: &TrelloShortCard,
|
||||
// nextcloud_card: &Card,
|
||||
// trello_attachment: &TrelloAttachment,
|
||||
// ) -> color_eyre::Result<()> {
|
||||
// let trello_client: TrelloClient = ctx.trello_client();
|
||||
//
|
||||
// let deck_client: DeckClient = ctx.deck_client();
|
||||
//
|
||||
// p!(ctx.prt, ">> Adding attachment: {}", trello_attachment.name);
|
||||
// // - - - download the attachment from trello
|
||||
// let attachment_path = trello_client
|
||||
// .save_attachment(
|
||||
// &trello_card.id,
|
||||
// &TrelloAttachmentId::new(&trello_attachment.id),
|
||||
// Some(&PathBuf::from(&trello_attachment.id)),
|
||||
// )
|
||||
// .await?;
|
||||
// let attachment_file = ctx.fs.file(&attachment_path);
|
||||
// // - - - upload the attachment to nextcloud card
|
||||
// let attachment = deck_client
|
||||
// .add_attachment_to_card(
|
||||
// nextcloud_board_id,
|
||||
// nextcloud_stack_id,
|
||||
// nextcloud_card.id,
|
||||
// &attachment_file,
|
||||
// )
|
||||
// .await
|
||||
// .result?;
|
||||
// p!(
|
||||
// ctx.prt,
|
||||
// ">> Attachment added: {}:{}",
|
||||
// attachment.id,
|
||||
// attachment.attachment_type,
|
||||
// // attachment.extended_data.mimetype
|
||||
// );
|
||||
// // delete local copy of attachment
|
||||
// attachment_file.remove()?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub(crate) struct ImportAttachmentActor {
|
||||
ctx: FullCtx,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
trello_card: &TrelloShortCard,
|
||||
nextcloud_card: &Card,
|
||||
trello_attachment: &TrelloAttachment,
|
||||
) -> color_eyre::Result<()> {
|
||||
let trello_client: TrelloClient = ctx.trello_client();
|
||||
|
||||
let deck_client: DeckClient = ctx.deck_client();
|
||||
|
||||
p!(ctx.prt, ">> Adding attachment: {}", trello_attachment.name);
|
||||
// - - - download the attachment from trello
|
||||
let attachment_path = trello_client
|
||||
.save_attachment(
|
||||
&trello_card.id,
|
||||
&TrelloAttachmentId::new(&trello_attachment.id),
|
||||
Some(&PathBuf::from(&trello_attachment.id)),
|
||||
)
|
||||
.await?;
|
||||
let attachment_file = ctx.fs.file(&attachment_path);
|
||||
// - - - upload the attachment to nextcloud card
|
||||
let attachment = deck_client
|
||||
.add_attachment_to_card(
|
||||
nextcloud_card_id: NextcloudCardId,
|
||||
trello_card_id: TrelloCardId,
|
||||
trello_attachment: TrelloAttachment,
|
||||
}
|
||||
impl ImportAttachmentActor {
|
||||
pub fn new(
|
||||
ctx: FullCtx,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
nextcloud_card_id: NextcloudCardId,
|
||||
trello_card_id: TrelloCardId,
|
||||
trello_attachment: TrelloAttachment,
|
||||
) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
nextcloud_card.id,
|
||||
nextcloud_card_id,
|
||||
trello_card_id,
|
||||
trello_attachment,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Actor for ImportAttachmentActor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
on_actor_start!(this, actor_ref, {
|
||||
p!(
|
||||
this.ctx.prt,
|
||||
">> Adding attachment: {}",
|
||||
this.trello_attachment.name
|
||||
);
|
||||
// - - - download the attachment from trello
|
||||
let attachment_path = this
|
||||
.ctx
|
||||
.trello_client()
|
||||
.save_attachment(
|
||||
&this.trello_card_id,
|
||||
&TrelloAttachmentId::new(&this.trello_attachment.id),
|
||||
Some(&PathBuf::from(&this.trello_attachment.id)),
|
||||
)
|
||||
.await?;
|
||||
let attachment_file = this.ctx.fs.file(&attachment_path);
|
||||
// - - - upload the attachment to nextcloud card
|
||||
let attachment = this
|
||||
.ctx
|
||||
.deck_client()
|
||||
.add_attachment_to_card(
|
||||
this.nextcloud_board_id,
|
||||
this.nextcloud_stack_id,
|
||||
this.nextcloud_card_id,
|
||||
&attachment_file,
|
||||
)
|
||||
.await
|
||||
.result?;
|
||||
p!(
|
||||
ctx.prt,
|
||||
this.ctx.prt,
|
||||
">> Attachment added: {}:{}",
|
||||
attachment.id,
|
||||
attachment.attachment_type,
|
||||
|
@ -58,5 +125,7 @@ pub(super) async fn import_attachment(
|
|||
);
|
||||
// delete local copy of attachment
|
||||
attachment_file.remove()?;
|
||||
Ok(())
|
||||
|
||||
Ok(actor_ref.stop_gracefully().await?)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,75 +1,194 @@
|
|||
//
|
||||
use std::collections::HashMap;
|
||||
|
||||
use kameo::{
|
||||
actor::{ActorID, ActorRef},
|
||||
error::ActorStopReason,
|
||||
mailbox::unbounded::UnboundedMailbox,
|
||||
Actor,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
import::{attachment, label},
|
||||
import::{attachment::ImportAttachmentActor, label::ImportLabelActor, labels::LabelsActor},
|
||||
nextcloud::{
|
||||
client::DeckClient,
|
||||
model::{
|
||||
Label, NextcloudBoardId, NextcloudCardDescription, NextcloudCardTitle,
|
||||
NextcloudLabelTitle, NextcloudStackId,
|
||||
model::{NextcloudBoardId, NextcloudCardDescription, NextcloudCardTitle, NextcloudStackId},
|
||||
},
|
||||
},
|
||||
p,
|
||||
on_actor_link_died, on_actor_start, p, spawn_in_thread,
|
||||
trello::{client::TrelloClient, model::card::TrelloShortCard},
|
||||
FullCtx,
|
||||
};
|
||||
|
||||
pub(super) async fn import_card(
|
||||
ctx: &FullCtx,
|
||||
// pub(super) async fn import_card(
|
||||
// ctx: &FullCtx,
|
||||
// nextcloud_board_id: NextcloudBoardId,
|
||||
// nextcloud_labels: &mut HashMap<NextcloudLabelTitle, Label>,
|
||||
// nextcloud_stack_id: NextcloudStackId,
|
||||
// trello_card: &TrelloShortCard,
|
||||
// ) -> color_eyre::Result<()> {
|
||||
// let trello_client: TrelloClient = ctx.trello_client();
|
||||
//
|
||||
// let deck_client: DeckClient = ctx.deck_client();
|
||||
//
|
||||
// p!(ctx.prt, "> Importing card: {}", trello_card.name);
|
||||
// // - - create a nextcloud card
|
||||
// let title = NextcloudCardTitle::from(&trello_card.name);
|
||||
// let desc: Option<NextcloudCardDescription> = match trello_card.desc.len() {
|
||||
// 0 => None,
|
||||
// _ => Some(NextcloudCardDescription::from(&trello_card.desc)),
|
||||
// };
|
||||
// let nextcloud_card = deck_client
|
||||
// .create_card(
|
||||
// nextcloud_board_id,
|
||||
// nextcloud_stack_id,
|
||||
// &title,
|
||||
// desc.as_ref(),
|
||||
// )
|
||||
// .await
|
||||
// .result?;
|
||||
// // - - for each label on the trello card
|
||||
// for trello_label in trello_card.labels.iter() {
|
||||
// labels::import_label(
|
||||
// ctx,
|
||||
// nextcloud_board_id,
|
||||
// nextcloud_labels,
|
||||
// nextcloud_stack_id,
|
||||
// &nextcloud_card,
|
||||
// trello_label,
|
||||
// )
|
||||
// .await?;
|
||||
// }
|
||||
// // - - for each attachment on the trello card
|
||||
// let attachments = trello_client
|
||||
// .card(&trello_card.id)
|
||||
// .await
|
||||
// .result?
|
||||
// .attachments;
|
||||
// for trello_attachment in attachments.iter() {
|
||||
// attachment::import_attachment(
|
||||
// ctx,
|
||||
// nextcloud_board_id,
|
||||
// nextcloud_stack_id,
|
||||
// trello_card,
|
||||
// &nextcloud_card,
|
||||
// trello_attachment,
|
||||
// )
|
||||
// .await?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub(crate) struct ImportCardActor {
|
||||
ctx: FullCtx,
|
||||
trello_card: TrelloShortCard,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_labels: &mut HashMap<NextcloudLabelTitle, Label>,
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
trello_card: &TrelloShortCard,
|
||||
) -> color_eyre::Result<()> {
|
||||
let trello_client: TrelloClient = ctx.trello_client();
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
|
||||
let deck_client: DeckClient = ctx.deck_client();
|
||||
labels_children: HashMap<ActorID, ActorRef<ImportLabelActor>>,
|
||||
attachments_children: HashMap<ActorID, ActorRef<ImportAttachmentActor>>,
|
||||
}
|
||||
impl ImportCardActor {
|
||||
pub(crate) fn new(
|
||||
ctx: FullCtx,
|
||||
trello_card: TrelloShortCard,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
trello_card,
|
||||
nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
labels_actor_ref,
|
||||
|
||||
p!(ctx.prt, "> Importing card: {}", trello_card.name);
|
||||
labels_children: Default::default(),
|
||||
attachments_children: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Actor for ImportCardActor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
on_actor_start!(this, actor_ref, {
|
||||
let trello_client: TrelloClient = this.ctx.trello_client();
|
||||
|
||||
let deck_client: DeckClient = this.ctx.deck_client();
|
||||
|
||||
p!(this.ctx.prt, "> Importing card: {}", this.trello_card.name);
|
||||
// - - create a nextcloud card
|
||||
let title = NextcloudCardTitle::from(&trello_card.name);
|
||||
let desc: Option<NextcloudCardDescription> = match trello_card.desc.len() {
|
||||
let title = NextcloudCardTitle::from(&this.trello_card.name);
|
||||
let desc: Option<NextcloudCardDescription> = match this.trello_card.desc.len() {
|
||||
0 => None,
|
||||
_ => Some(NextcloudCardDescription::from(&trello_card.desc)),
|
||||
_ => Some(NextcloudCardDescription::from(&this.trello_card.desc)),
|
||||
};
|
||||
let nextcloud_card = deck_client
|
||||
.create_card(
|
||||
nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
this.nextcloud_board_id,
|
||||
this.nextcloud_stack_id,
|
||||
&title,
|
||||
desc.as_ref(),
|
||||
)
|
||||
.await
|
||||
.result?;
|
||||
|
||||
// - - for each label on the trello card
|
||||
for trello_label in trello_card.labels.iter() {
|
||||
label::import_label(
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
nextcloud_labels,
|
||||
nextcloud_stack_id,
|
||||
&nextcloud_card,
|
||||
let mut labels = vec![];
|
||||
std::mem::swap(&mut this.trello_card.labels, &mut labels);
|
||||
for trello_label in labels.into_iter() {
|
||||
let child = spawn_in_thread!(
|
||||
actor_ref,
|
||||
ImportLabelActor::new(
|
||||
this.ctx.clone(),
|
||||
this.nextcloud_board_id,
|
||||
this.nextcloud_stack_id,
|
||||
nextcloud_card.id,
|
||||
trello_label,
|
||||
this.labels_actor_ref.clone(),
|
||||
)
|
||||
.await?;
|
||||
);
|
||||
this.labels_children.insert(child.id(), child.clone());
|
||||
}
|
||||
// - - for each attachment on the trello card
|
||||
|
||||
let attachments = trello_client
|
||||
.card(&trello_card.id)
|
||||
.card(&this.trello_card.id)
|
||||
.await
|
||||
.result?
|
||||
.attachments;
|
||||
for trello_attachment in attachments.iter() {
|
||||
attachment::import_attachment(
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
trello_card,
|
||||
&nextcloud_card,
|
||||
for trello_attachment in attachments.into_iter() {
|
||||
let child = spawn_in_thread!(
|
||||
actor_ref,
|
||||
ImportAttachmentActor::new(
|
||||
this.ctx.clone(),
|
||||
this.nextcloud_board_id,
|
||||
this.nextcloud_stack_id,
|
||||
nextcloud_card.id,
|
||||
this.trello_card.id.clone(),
|
||||
trello_attachment,
|
||||
)
|
||||
.await?;
|
||||
);
|
||||
this.attachments_children.insert(child.id(), child.clone());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
on_actor_link_died!(this, actor_ref, id, reason, {
|
||||
match reason {
|
||||
ActorStopReason::Normal => {
|
||||
this.labels_children.remove(&id);
|
||||
this.attachments_children.remove(&id);
|
||||
}
|
||||
_ => {
|
||||
return Ok(Some(reason));
|
||||
}
|
||||
}
|
||||
|
||||
if this.labels_children.is_empty() && this.attachments_children.is_empty() {
|
||||
return Ok(Some(ActorStopReason::Normal));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,67 +1,65 @@
|
|||
//
|
||||
use std::collections::HashMap;
|
||||
|
||||
use all_colors::get_color_hex;
|
||||
use kameo::{actor::ActorRef, mailbox::unbounded::UnboundedMailbox, Actor};
|
||||
|
||||
use crate::{
|
||||
nextcloud::{
|
||||
client::DeckClient,
|
||||
model::{
|
||||
Card, Label, NextcloudBoardId, NextcloudLabelColour, NextcloudLabelTitle,
|
||||
NextcloudStackId,
|
||||
},
|
||||
},
|
||||
p,
|
||||
ask,
|
||||
import::{labels, labels::LabelsActor},
|
||||
nextcloud::model::{NextcloudBoardId, NextcloudCardId, NextcloudStackId},
|
||||
on_actor_start,
|
||||
trello::model::label::TrelloLabel,
|
||||
FullCtx,
|
||||
};
|
||||
|
||||
pub(super) async fn import_label(
|
||||
ctx: &FullCtx,
|
||||
pub(crate) struct ImportLabelActor {
|
||||
ctx: FullCtx,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_labels: &mut HashMap<NextcloudLabelTitle, Label>,
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
nextcloud_card: &Card,
|
||||
trello_label: &TrelloLabel,
|
||||
) -> color_eyre::Result<()> {
|
||||
let deck_client: DeckClient = ctx.deck_client();
|
||||
|
||||
p!(
|
||||
ctx.prt,
|
||||
">> Adding label: {} ({})",
|
||||
trello_label.name,
|
||||
trello_label.color
|
||||
);
|
||||
// - - - find the equivalent label in nextcloud
|
||||
let nextcloud_label: &Label =
|
||||
match nextcloud_labels.get(&NextcloudLabelTitle::new(trello_label.name.as_ref())) {
|
||||
Some(label) => label,
|
||||
None => {
|
||||
p!(ctx.prt, ">> Label not found in nextcloud board, creating");
|
||||
|
||||
let label = deck_client
|
||||
.create_label(
|
||||
nextcloud_board_id,
|
||||
&NextcloudLabelTitle::new(trello_label.name.as_ref()),
|
||||
&NextcloudLabelColour::new(get_color_hex(trello_label.color.as_ref())),
|
||||
)
|
||||
.await
|
||||
.result?;
|
||||
nextcloud_labels.insert(label.title.clone(), label);
|
||||
nextcloud_labels
|
||||
.get(&NextcloudLabelTitle::new(trello_label.name.as_ref()))
|
||||
.expect("label was just inserted")
|
||||
}
|
||||
};
|
||||
// - - - add the label to the nextcloud card
|
||||
deck_client
|
||||
.add_label_to_card(
|
||||
nextcloud_card_id: NextcloudCardId,
|
||||
trello_label: TrelloLabel,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
}
|
||||
impl ImportLabelActor {
|
||||
pub(crate) fn new(
|
||||
ctx: FullCtx,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
nextcloud_card_id: NextcloudCardId,
|
||||
trello_label: TrelloLabel,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
nextcloud_card.id,
|
||||
nextcloud_label.id,
|
||||
nextcloud_card_id,
|
||||
trello_label,
|
||||
labels_actor_ref,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Actor for ImportLabelActor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
on_actor_start!(this, actor_ref, {
|
||||
let label_id = ask!(
|
||||
this.labels_actor_ref,
|
||||
labels::LookupNextcloudLabelId {
|
||||
trello_name: this.trello_label.name.clone(),
|
||||
trello_color: this.trello_label.color.clone()
|
||||
}
|
||||
)?;
|
||||
// - - - add the label to the nextcloud card
|
||||
this.ctx
|
||||
.deck_client()
|
||||
.add_label_to_card(
|
||||
this.nextcloud_board_id,
|
||||
this.nextcloud_stack_id,
|
||||
this.nextcloud_card_id,
|
||||
label_id,
|
||||
)
|
||||
.await
|
||||
.result?;
|
||||
Ok(())
|
||||
|
||||
Ok(actor_ref.stop_gracefully().await?)
|
||||
});
|
||||
}
|
||||
|
|
142
src/import/labels.rs
Normal file
142
src/import/labels.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
//
|
||||
use std::collections::HashMap;
|
||||
|
||||
use all_colors::get_color_hex;
|
||||
use kameo::{
|
||||
mailbox::unbounded::UnboundedMailbox,
|
||||
message::{Context, Message},
|
||||
Actor,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
nextcloud::model::{
|
||||
NextcloudBoardId, NextcloudLabelColour, NextcloudLabelId, NextcloudLabelTitle,
|
||||
},
|
||||
on_actor_start, p,
|
||||
trello::model::label::{TrelloLabelColor, TrelloLabelName},
|
||||
FullCtx,
|
||||
};
|
||||
|
||||
// pub(super) async fn import_label(
|
||||
// ctx: &FullCtx,
|
||||
// nextcloud_board_id: NextcloudBoardId,
|
||||
// nextcloud_labels: &mut HashMap<NextcloudLabelTitle, Label>,
|
||||
// nextcloud_stack_id: NextcloudStackId,
|
||||
// nextcloud_card: &Card,
|
||||
// trello_label: &TrelloLabel,
|
||||
// ) -> color_eyre::Result<()> {
|
||||
// let deck_client: DeckClient = ctx.deck_client();
|
||||
//
|
||||
// p!(
|
||||
// ctx.prt,
|
||||
// ">> Adding label: {} ({})",
|
||||
// trello_label.name,
|
||||
// trello_label.color
|
||||
// );
|
||||
// // - - - find the equivalent label in nextcloud
|
||||
// let nextcloud_label: &Label =
|
||||
// match nextcloud_labels.get(&NextcloudLabelTitle::new(trello_label.name.as_ref())) {
|
||||
// Some(label) => label,
|
||||
// None => {
|
||||
// p!(ctx.prt, ">> Label not found in nextcloud board, creating");
|
||||
//
|
||||
// let label = deck_client
|
||||
// .create_label(
|
||||
// nextcloud_board_id,
|
||||
// &NextcloudLabelTitle::new(trello_label.name.as_ref()),
|
||||
// &NextcloudLabelColour::new(get_color_hex(trello_label.color.as_ref())),
|
||||
// )
|
||||
// .await
|
||||
// .result?;
|
||||
// nextcloud_labels.insert(label.title.clone(), label);
|
||||
// nextcloud_labels
|
||||
// .get(&NextcloudLabelTitle::new(trello_label.name.as_ref()))
|
||||
// .expect("label was just inserted")
|
||||
// }
|
||||
// };
|
||||
// // - - - add the label to the nextcloud card
|
||||
// deck_client
|
||||
// .add_label_to_card(
|
||||
// nextcloud_board_id,
|
||||
// nextcloud_stack_id,
|
||||
// nextcloud_card.id,
|
||||
// nextcloud_label.id,
|
||||
// )
|
||||
// .await
|
||||
// .result?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub(super) struct LabelsActor {
|
||||
ctx: FullCtx,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
lookup: HashMap<NextcloudLabelTitle, NextcloudLabelId>,
|
||||
}
|
||||
impl LabelsActor {
|
||||
pub(super) fn new(ctx: FullCtx, nextcloud_board_id: NextcloudBoardId) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
lookup: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Actor for LabelsActor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
on_actor_start!(this, actor_ref, {
|
||||
this.ctx
|
||||
.deck_client()
|
||||
.get_board(this.nextcloud_board_id)
|
||||
.await
|
||||
.result?
|
||||
.labels
|
||||
.iter()
|
||||
.for_each(|l| {
|
||||
this.lookup.insert(l.title.clone(), l.id);
|
||||
});
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) struct LookupNextcloudLabelId {
|
||||
pub(crate) trello_name: TrelloLabelName,
|
||||
pub(crate) trello_color: TrelloLabelColor,
|
||||
}
|
||||
impl Message<LookupNextcloudLabelId> for LabelsActor {
|
||||
type Reply = Result<NextcloudLabelId, kxio::Error>;
|
||||
async fn handle(
|
||||
&mut self,
|
||||
msg: LookupNextcloudLabelId,
|
||||
_ctx: Context<'_, Self, Self::Reply>,
|
||||
) -> Result<NextcloudLabelId, kxio::Error> {
|
||||
let nextcloud_label_id = match self
|
||||
.lookup
|
||||
.get(&NextcloudLabelTitle::new(msg.trello_name.as_ref()))
|
||||
{
|
||||
Some(nextcloud_label_id) => nextcloud_label_id,
|
||||
None => {
|
||||
p!(
|
||||
self.ctx.prt,
|
||||
">> Label not found in nextcloud board, creating"
|
||||
);
|
||||
|
||||
let label = self
|
||||
.ctx
|
||||
.deck_client()
|
||||
.create_label(
|
||||
self.nextcloud_board_id,
|
||||
&NextcloudLabelTitle::new(msg.trello_name.as_ref()),
|
||||
&NextcloudLabelColour::new(get_color_hex(msg.trello_color.as_ref())),
|
||||
)
|
||||
.await
|
||||
.result?;
|
||||
self.lookup.insert(label.title.clone(), label.id);
|
||||
self.lookup
|
||||
.get(&NextcloudLabelTitle::new(msg.trello_name.as_ref()))
|
||||
.expect("label was just inserted")
|
||||
}
|
||||
};
|
||||
Ok(*nextcloud_label_id)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
//
|
||||
use std::collections::HashMap;
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use kameo::actor::spawn_in_thread;
|
||||
|
||||
use crate::{
|
||||
f,
|
||||
nextcloud::model::{Label, NextcloudBoardTitle, NextcloudLabelTitle},
|
||||
import::{labels::LabelsActor, stacks::ImportStacksActor, supervisor::Supervisor},
|
||||
nextcloud::model::NextcloudBoardTitle,
|
||||
spawn_in_thread,
|
||||
trello::model::board::{TrelloBoardId, TrelloBoardName, TrelloBoards},
|
||||
FullCtx,
|
||||
};
|
||||
|
@ -13,7 +14,11 @@ use crate::{
|
|||
mod attachment;
|
||||
mod card;
|
||||
mod label;
|
||||
mod labels;
|
||||
mod spawn;
|
||||
mod stack;
|
||||
mod stacks;
|
||||
mod supervisor;
|
||||
|
||||
pub(crate) async fn run(ctx: &FullCtx) -> color_eyre::Result<()> {
|
||||
// get list of trello boards
|
||||
|
@ -72,45 +77,26 @@ pub(crate) async fn run(ctx: &FullCtx) -> color_eyre::Result<()> {
|
|||
.find(|b| b.title == nextcloud_board_name)
|
||||
.map(|b| b.id)
|
||||
.expect("find selected board");
|
||||
// get list of nextcloud stacks in the selected board
|
||||
let nextcloud_stacks = deck_client.get_stacks(nextcloud_board_id).await.result?;
|
||||
|
||||
stack::create_any_missing_stacks(
|
||||
ctx,
|
||||
&selected_trello_stack_names,
|
||||
let supervisor = spawn_in_thread(Supervisor);
|
||||
|
||||
let labels_actor_ref = spawn_in_thread!(
|
||||
supervisor,
|
||||
LabelsActor::new(ctx.clone(), nextcloud_board_id)
|
||||
);
|
||||
|
||||
let _main = spawn_in_thread!(
|
||||
supervisor,
|
||||
ImportStacksActor::new(
|
||||
ctx.clone(),
|
||||
trello_stacks.clone(),
|
||||
selected_trello_stack_names.into_iter().cloned().collect(),
|
||||
nextcloud_board_id,
|
||||
nextcloud_stacks,
|
||||
labels_actor_ref.clone(),
|
||||
)
|
||||
.await?;
|
||||
);
|
||||
|
||||
// - get the list of nextcloud stacks again (with new stack ids)
|
||||
let nextcloud_stacks = deck_client.get_stacks(nextcloud_board_id).await.result?;
|
||||
|
||||
let mut nextcloud_labels: HashMap<NextcloudLabelTitle, Label> = deck_client
|
||||
.get_board(nextcloud_board_id)
|
||||
.await
|
||||
.result?
|
||||
.labels
|
||||
.iter()
|
||||
.map(|l| (l.title.clone(), l.clone()))
|
||||
.collect();
|
||||
|
||||
// for each selected trello stack
|
||||
for selected_trello_stack in trello_stacks
|
||||
.iter()
|
||||
.filter(|s| selected_trello_stack_names.contains(&s.name.as_ref()))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
{
|
||||
stack::import_stack(
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
&nextcloud_stacks,
|
||||
&mut nextcloud_labels,
|
||||
selected_trello_stack,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
supervisor.wait_for_stop().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
25
src/import/spawn.rs
Normal file
25
src/import/spawn.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
/// spawns a new actor and sets up bi-directional links
|
||||
#[macro_export]
|
||||
macro_rules! spawn {
|
||||
($parent:expr, $actor:expr) => {{
|
||||
tracing::debug!("spawning : {}", stringify!($actor));
|
||||
let new_actor_ref = kameo::spawn($actor);
|
||||
new_actor_ref.link(&$parent).await;
|
||||
$parent.link(&new_actor_ref).await;
|
||||
tracing::debug!("spawned : {}", stringify!($actor));
|
||||
new_actor_ref
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! spawn_in_thread {
|
||||
($parent:expr, $actor:expr) => {{
|
||||
tracing::debug!("spawning in thread : {}", stringify!($actor));
|
||||
let new_actor_ref = kameo::actor::spawn_in_thread($actor);
|
||||
new_actor_ref.link(&$parent).await;
|
||||
$parent.link(&new_actor_ref).await;
|
||||
tracing::debug!("spawned in thread : {}", stringify!($actor));
|
||||
new_actor_ref
|
||||
}};
|
||||
}
|
|
@ -1,13 +1,17 @@
|
|||
//
|
||||
use std::{collections::HashMap, ops::Deref};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use kameo::{
|
||||
actor::{ActorID, ActorRef},
|
||||
error::ActorStopReason,
|
||||
mailbox::unbounded::UnboundedMailbox,
|
||||
Actor,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
import::card,
|
||||
nextcloud::{
|
||||
client::DeckClient,
|
||||
model::{Label, NextcloudBoardId, NextcloudLabelTitle, Stack},
|
||||
},
|
||||
p,
|
||||
import::{card::ImportCardActor, labels::LabelsActor},
|
||||
nextcloud::model::{NextcloudBoardId, NextcloudStackId},
|
||||
on_actor_link_died, on_actor_start, spawn_in_thread,
|
||||
trello::{
|
||||
client::TrelloClient,
|
||||
model::list::{TrelloList, TrelloListId},
|
||||
|
@ -15,68 +19,74 @@ use crate::{
|
|||
FullCtx,
|
||||
};
|
||||
|
||||
pub(super) async fn create_any_missing_stacks(
|
||||
ctx: &FullCtx,
|
||||
selected_trello_stack_names: &Vec<&String>,
|
||||
pub(super) struct ImportStackActor {
|
||||
ctx: FullCtx,
|
||||
trello_stack: TrelloList,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stacks: Vec<Stack>,
|
||||
) -> 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
|
||||
.iter()
|
||||
.filter(|s| !nextcloud_stacks.iter().any(|ns| ns.title.deref() == **s))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if !missing_stack_names.is_empty() {
|
||||
crate::p!(ctx.prt, "Missing stacks: {:?}", missing_stack_names);
|
||||
// create any missing stacks in nextcloud
|
||||
// for each missing stack
|
||||
for missing_stack_name in missing_stack_names.into_iter() {
|
||||
// - create the stack
|
||||
let stack = deck_client
|
||||
.create_stack(nextcloud_board_id, &missing_stack_name.clone().into())
|
||||
.await
|
||||
.result?;
|
||||
p!(ctx.prt, "Created stack: {}", stack.title);
|
||||
}
|
||||
p!(ctx.prt, "Stacks created");
|
||||
}
|
||||
Ok(())
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
children: HashMap<ActorID, ActorRef<ImportCardActor>>,
|
||||
}
|
||||
|
||||
pub(super) async fn import_stack(
|
||||
ctx: &FullCtx,
|
||||
impl ImportStackActor {
|
||||
pub(crate) fn new(
|
||||
ctx: FullCtx,
|
||||
trello_stack: TrelloList,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stacks: &Vec<Stack>,
|
||||
nextcloud_labels: &mut HashMap<NextcloudLabelTitle, Label>,
|
||||
selected_trello_stack: &TrelloList,
|
||||
) -> color_eyre::Result<()> {
|
||||
let trello_client: TrelloClient = ctx.trello_client();
|
||||
nextcloud_stack_id: NextcloudStackId,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
trello_stack,
|
||||
nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
labels_actor_ref,
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Actor for ImportStackActor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
p!(ctx.prt, "Importing stack: {}", selected_trello_stack.name);
|
||||
let nextcloud_stack_id = nextcloud_stacks
|
||||
.iter()
|
||||
.find(|ns| ns.title.deref() == selected_trello_stack.name.as_ref())
|
||||
.map(|ns| ns.id)
|
||||
.expect("find nextcloud stack");
|
||||
on_actor_start!(this, actor_ref, {
|
||||
let trello_client: TrelloClient = this.ctx.trello_client();
|
||||
|
||||
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
|
||||
.list_cards(&TrelloListId::new(selected_trello_stack.id.as_ref()))
|
||||
.list_cards(&TrelloListId::new(this.trello_stack.id.as_ref()))
|
||||
.await
|
||||
.result?;
|
||||
|
||||
// - for each card in the trello stack
|
||||
for trello_card in trello_cards.into_iter() {
|
||||
card::import_card(
|
||||
ctx,
|
||||
nextcloud_board_id,
|
||||
nextcloud_labels,
|
||||
nextcloud_stack_id,
|
||||
&trello_card,
|
||||
let child: ActorRef<ImportCardActor> = spawn_in_thread!(
|
||||
actor_ref.clone(),
|
||||
ImportCardActor::new(
|
||||
this.ctx.clone(),
|
||||
trello_card,
|
||||
this.nextcloud_board_id,
|
||||
this.nextcloud_stack_id,
|
||||
this.labels_actor_ref.clone(),
|
||||
)
|
||||
.await?;
|
||||
);
|
||||
this.children.insert(child.id(), child.clone());
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
on_actor_link_died!(this, actor_ref, id, reason, {
|
||||
match reason {
|
||||
ActorStopReason::Normal => {
|
||||
this.children.remove(&id);
|
||||
}
|
||||
_ => {
|
||||
return Ok(Some(reason));
|
||||
}
|
||||
}
|
||||
if this.children.is_empty() {
|
||||
return Ok(Some(ActorStopReason::Normal));
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
}
|
||||
|
|
150
src/import/stacks.rs
Normal file
150
src/import/stacks.rs
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
use std::{collections::HashMap, ops::Deref};
|
||||
|
||||
use kameo::{
|
||||
actor::{ActorID, ActorRef},
|
||||
error::ActorStopReason,
|
||||
mailbox::unbounded::UnboundedMailbox,
|
||||
Actor,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
import::{labels::LabelsActor, stack::ImportStackActor},
|
||||
nextcloud::{
|
||||
client::DeckClient,
|
||||
model::{NextcloudBoardId, Stack},
|
||||
},
|
||||
on_actor_link_died, on_actor_start, p, spawn_in_thread,
|
||||
trello::model::list::TrelloList,
|
||||
FullCtx,
|
||||
};
|
||||
|
||||
async fn create_any_missing_stacks(
|
||||
ctx: &FullCtx,
|
||||
selected_trello_stack_names: &[String],
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
nextcloud_stacks: Vec<Stack>,
|
||||
) -> 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
|
||||
.iter()
|
||||
.filter(|s| !nextcloud_stacks.iter().any(|ns| ns.title.deref() == *s))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if !missing_stack_names.is_empty() {
|
||||
crate::p!(ctx.prt, "Missing stacks: {:?}", missing_stack_names);
|
||||
// create any missing stacks in nextcloud
|
||||
// for each missing stack
|
||||
for missing_stack_name in missing_stack_names.into_iter() {
|
||||
// - create the stack
|
||||
let stack = deck_client
|
||||
.create_stack(nextcloud_board_id, &missing_stack_name.clone().into())
|
||||
.await
|
||||
.result?;
|
||||
p!(ctx.prt, "Created stack: {}", stack.title);
|
||||
}
|
||||
p!(ctx.prt, "Stacks created");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//
|
||||
pub(super) struct ImportStacksActor {
|
||||
ctx: FullCtx,
|
||||
trello_stacks: Vec<TrelloList>,
|
||||
selected_trello_stack_names: Vec<String>,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
|
||||
children: HashMap<ActorID, ActorRef<ImportStackActor>>,
|
||||
}
|
||||
impl ImportStacksActor {
|
||||
pub(super) fn new(
|
||||
ctx: FullCtx,
|
||||
trello_stacks: Vec<TrelloList>,
|
||||
selected_trello_stack_names: Vec<String>,
|
||||
nextcloud_board_id: NextcloudBoardId,
|
||||
labels_actor_ref: ActorRef<LabelsActor>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
trello_stacks,
|
||||
selected_trello_stack_names,
|
||||
nextcloud_board_id,
|
||||
labels_actor_ref,
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Actor for ImportStacksActor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
on_actor_start!(this, actor_ref, {
|
||||
// spawn a new ImportStack actor for each trello_stack named in selected_trello_stack_names
|
||||
|
||||
// get list of nextcloud stacks in the selected board
|
||||
let nextcloud_stacks = this
|
||||
.ctx
|
||||
.deck_client()
|
||||
.get_stacks(this.nextcloud_board_id)
|
||||
.await
|
||||
.result?;
|
||||
create_any_missing_stacks(
|
||||
&this.ctx,
|
||||
&this.selected_trello_stack_names,
|
||||
this.nextcloud_board_id,
|
||||
nextcloud_stacks,
|
||||
)
|
||||
.await?;
|
||||
// - get the list of nextcloud stacks again (with new stack ids)
|
||||
let nextcloud_stacks = this
|
||||
.ctx
|
||||
.deck_client()
|
||||
.get_stacks(this.nextcloud_board_id)
|
||||
.await
|
||||
.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()))
|
||||
{
|
||||
let nextcloud_stack_id = //this.
|
||||
nextcloud_stacks
|
||||
.iter()
|
||||
.find(|ns| ns.title.deref() == selected_trello_stack.name.as_ref())
|
||||
.map(|ns| ns.id)
|
||||
.expect("find nextcloud stack");
|
||||
let child: ActorRef<ImportStackActor> = spawn_in_thread!(
|
||||
actor_ref,
|
||||
ImportStackActor::new(
|
||||
this.ctx.clone(),
|
||||
selected_trello_stack.clone(),
|
||||
this.nextcloud_board_id,
|
||||
nextcloud_stack_id,
|
||||
this.labels_actor_ref.clone(),
|
||||
)
|
||||
);
|
||||
this.children.insert(child.id(), child);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
on_actor_link_died!(this, actor_ref, id, reason, {
|
||||
match reason {
|
||||
ActorStopReason::Normal => {
|
||||
this.children.remove(&id);
|
||||
}
|
||||
_ => {
|
||||
return Ok(Some(reason));
|
||||
}
|
||||
}
|
||||
if this.children.is_empty() {
|
||||
return Ok(Some(ActorStopReason::Normal));
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
}
|
22
src/import/supervisor.rs
Normal file
22
src/import/supervisor.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
use kameo::{
|
||||
actor::{ActorID, WeakActorRef},
|
||||
error::{ActorStopReason, BoxError},
|
||||
mailbox::unbounded::UnboundedMailbox,
|
||||
Actor,
|
||||
};
|
||||
|
||||
pub(super) struct Supervisor;
|
||||
impl Actor for Supervisor {
|
||||
type Mailbox = UnboundedMailbox<Self>;
|
||||
|
||||
async fn on_link_died(
|
||||
&mut self,
|
||||
_actor_ref: WeakActorRef<Self>,
|
||||
_id: ActorID,
|
||||
reason: ActorStopReason,
|
||||
) -> Result<Option<ActorStopReason>, BoxError> {
|
||||
// normally when ImportStacksActor stops it would not cause the supervisor to stop, but we want it to stop.
|
||||
Ok(Some(reason))
|
||||
}
|
||||
}
|
15
src/lib.rs
15
src/lib.rs
|
@ -4,7 +4,11 @@ use std::path::PathBuf;
|
|||
use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use config::AppConfig;
|
||||
use kxio::{fs::FileSystem, net::Net, print::Printer};
|
||||
use kxio::{fs::FileSystem, kxeprintln as e, kxprintln as p, net::Net, print::Printer};
|
||||
|
||||
use crate::{nextcloud::client::DeckClient, trello::client::TrelloClient};
|
||||
|
||||
use execute::Execute;
|
||||
|
||||
mod api_result;
|
||||
mod check;
|
||||
|
@ -13,7 +17,7 @@ mod conversion;
|
|||
mod execute;
|
||||
mod import;
|
||||
mod init;
|
||||
mod macros;
|
||||
pub mod macros;
|
||||
mod nextcloud;
|
||||
mod template;
|
||||
mod trello;
|
||||
|
@ -23,13 +27,6 @@ mod tests;
|
|||
|
||||
const NAME: &str = "trello-to-deck";
|
||||
|
||||
use crate::nextcloud::client::DeckClient;
|
||||
use crate::trello::client::TrelloClient;
|
||||
use execute::Execute;
|
||||
|
||||
use kxio::kxeprintln as e;
|
||||
use kxio::kxprintln as p;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version = clap::crate_version!(), author = clap::crate_authors!(), about = clap::crate_description!())]
|
||||
pub struct Commands {
|
||||
|
|
209
src/macros/actor.rs
Normal file
209
src/macros/actor.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
//
|
||||
|
||||
/// Called when the actor starts, before it processes any messages.
|
||||
///
|
||||
/// Messages sent internally by the actor during `on_start` are prioritized and processed
|
||||
/// before any externally sent messages, even if external messages are received first.
|
||||
///
|
||||
/// This ensures that the actor can properly initialize before handling external messages.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use trello_to_deck::on_actor_start;
|
||||
/// struct ServerActor;
|
||||
/// impl kameo::Actor for ServerActor {
|
||||
/// type Mailbox = kameo::mailbox::unbounded::UnboundedMailbox<Self>;
|
||||
///
|
||||
/// on_actor_start!(this, actor_ref, {
|
||||
/// // handle start here
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! on_actor_start {
|
||||
($this:ident, $actor_ref:ident, $body:expr) => {
|
||||
#[allow(unused_variables)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn on_start(
|
||||
&mut self,
|
||||
actor_ref: kameo::actor::ActorRef<Self>,
|
||||
) -> std::result::Result<(), kameo::error::BoxError> {
|
||||
tracing::debug!(?actor_ref, "{}", <Self as kameo::Actor>::name());
|
||||
let $this = self;
|
||||
let $actor_ref = actor_ref;
|
||||
$body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_on_actor_start {
|
||||
($this:ident, $actor_ref:ident) => {
|
||||
$crate::on_actor_start!($this, $actor_ref, { Ok(()) });
|
||||
};
|
||||
}
|
||||
|
||||
/// Called swhen the actor encounters a panic or an error during "tell" message handling.
|
||||
///
|
||||
/// This method gives the actor an opportunity to clean up or reset its state and determine
|
||||
/// whether it should be stopped or continue processing messages.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `err`: The panic or error that occurred.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Some(ActorStopReason)`: Stops the actor.
|
||||
/// - `None`: Allows the actor to continue processing messages.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use trello_to_deck::on_actor_panic;
|
||||
/// struct ServerActor;
|
||||
/// impl kameo::Actor for ServerActor {
|
||||
/// type Mailbox = kameo::mailbox::unbounded::UnboundedMailbox<Self>;
|
||||
///
|
||||
/// on_actor_panic!(this, actor_ref, err, {
|
||||
/// // handle panic here
|
||||
/// Ok(None)
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! on_actor_panic {
|
||||
($this:ident, $actor_ref:ident, $err:ident, $body:expr) => {
|
||||
#[allow(unused_variables)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn on_panic(
|
||||
&mut self,
|
||||
actor_ref: kameo::actor::WeakActorRef<Self>,
|
||||
err: kameo::error::PanicError,
|
||||
) -> std::result::Result<Option<kameo::error::ActorStopReason>, kameo::error::BoxError> {
|
||||
tracing::debug!(?actor_ref, %err, "{}", <Self as kameo::Actor>::name());
|
||||
let $this = self;
|
||||
let $actor_ref = actor_ref;
|
||||
let $err = err;
|
||||
$body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_on_actor_panic {
|
||||
($this:ident, $actor_ref:ident, $err:ident) => {
|
||||
$crate::on_actor_panic!($this, $actor_ref, $err, {
|
||||
Ok(Some(kameo::error::ActorStopReason::Panicked($err)))
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/// Called when a linked actor dies.
|
||||
///
|
||||
/// By default, the actor will stop if the reason for the linked actor's death is anything other
|
||||
/// than `Normal`. You can customize this behavior in the implementation.
|
||||
///
|
||||
/// # Returns
|
||||
/// Whether the actor should stop or continue processing messages.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use trello_to_deck::on_actor_link_died;
|
||||
/// struct ServerActor;
|
||||
/// impl kameo::Actor for ServerActor {
|
||||
/// type Mailbox = kameo::mailbox::unbounded::UnboundedMailbox<Self>;
|
||||
///
|
||||
/// on_actor_link_died!(this, actor_ref, id, reason, {
|
||||
/// // handle link death here
|
||||
/// Ok(None)
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! on_actor_link_died {
|
||||
($this:ident, $actor_ref:ident, $id:ident, $reason:ident, $body:expr) => {
|
||||
#[allow(unused_variables)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn on_link_died(
|
||||
&mut self,
|
||||
actor_ref: kameo::actor::WeakActorRef<Self>,
|
||||
id: kameo::actor::ActorID,
|
||||
reason: kameo::error::ActorStopReason,
|
||||
) -> std::result::Result<Option<kameo::error::ActorStopReason>, kameo::error::BoxError> {
|
||||
tracing::debug!(?actor_ref, %id, %reason, "{}", <Self as kameo::Actor>::name());
|
||||
let $this = self;
|
||||
let $actor_ref = actor_ref;
|
||||
let $id = id;
|
||||
let $reason = reason;
|
||||
$body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_on_actor_link_died {
|
||||
($this:ident, $actor_ref:ident, $id:ident, $reason:ident) => {
|
||||
$crate::on_actor_link_died!($this, $actor_ref, $id, $reason, {
|
||||
match &$reason {
|
||||
kameo::error::ActorStopReason::Normal => Ok(None),
|
||||
kameo::error::ActorStopReason::Killed
|
||||
| kameo::error::ActorStopReason::Panicked(_)
|
||||
| kameo::error::ActorStopReason::LinkDied { .. } => {
|
||||
Ok(Some(kameo::error::ActorStopReason::LinkDied {
|
||||
id: $id,
|
||||
reason: Box::new($reason),
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/// Called before the actor stops.
|
||||
///
|
||||
/// This allows the actor to perform any necessary cleanup or release resources before being fully stopped.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `reason`: The reason why the actor is being stopped.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use trello_to_deck::on_actor_stop;
|
||||
/// struct ServerActor;
|
||||
/// impl kameo::Actor for ServerActor {
|
||||
/// type Mailbox = kameo::mailbox::unbounded::UnboundedMailbox<Self>;
|
||||
///
|
||||
/// on_actor_stop!(this, actor_ref, reason, {
|
||||
/// // handle stop here
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! on_actor_stop {
|
||||
($this:ident, $actor_ref:ident, $reason:ident, $body:expr) => {
|
||||
#[allow(unused_variables)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn on_stop(
|
||||
&mut self,
|
||||
actor_ref: kameo::actor::WeakActorRef<Self>,
|
||||
reason: kameo::error::ActorStopReason,
|
||||
) -> std::result::Result<(), kameo::error::BoxError> {
|
||||
tracing::debug!(?actor_ref, %reason, "{}", <Self as kameo::Actor>::name());
|
||||
let $this = self;
|
||||
let $actor_ref = actor_ref;
|
||||
let $reason = reason;
|
||||
$body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_on_actor_stop {
|
||||
($this:ident, $actor_ref:ident, $reason:ident) => {
|
||||
$crate::on_actor_stop!($this, $actor_ref, $reason, { Ok(()) });
|
||||
};
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
//
|
||||
mod actor;
|
||||
mod newtype;
|
||||
mod print;
|
||||
mod send;
|
||||
mod to_string;
|
||||
|
|
64
src/macros/send.rs
Normal file
64
src/macros/send.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
#[macro_export]
|
||||
macro_rules! tell {
|
||||
($actor_ref:expr, $message:expr) => {
|
||||
tell!(stringify!($actor_ref), $actor_ref, $message)
|
||||
};
|
||||
($actor_name:expr, $actor_ref:expr, $message:expr) => {{
|
||||
tracing::debug!(actor = $actor_name, msg = stringify!($message), "send");
|
||||
$actor_ref.tell($message).await
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ask {
|
||||
($actor_ref:expr, $message:expr) => {
|
||||
ask!(stringify!($actor_ref), $actor_ref, $message)
|
||||
};
|
||||
($actor_name:expr, $actor_ref:expr, $message:expr) => {{
|
||||
tracing::debug!(actor = $actor_name, msg = stringify!($message), "send");
|
||||
$actor_ref.ask($message).await
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! subscribe {
|
||||
($message_bus:expr, $actor_ref:expr) => {
|
||||
subscribe!(
|
||||
stringify!($message_bus),
|
||||
$message_bus,
|
||||
stringify!($actor_ref),
|
||||
$actor_ref
|
||||
)
|
||||
};
|
||||
($message_bus:expr, $actor_name:expr, $actor_ref:expr) => {
|
||||
subscribe!(
|
||||
stringify!($message_bus),
|
||||
$message_bus,
|
||||
$actor_name,
|
||||
$actor_ref
|
||||
)
|
||||
};
|
||||
($bus_name:expr, $message_bus:expr, $actor_ref:expr) => {
|
||||
subscribe!($bus_name, $message_bus, stringify!($actor_ref), $actor_ref)
|
||||
};
|
||||
($bus_name:expr, $message_bus:expr, $actor_name:expr, $actor_ref:expr) => {{
|
||||
tracing::debug!(msg_bus = $bus_name, actor = $actor_name, "subscribe");
|
||||
$message_bus
|
||||
.tell(kameo::actor::pubsub::Subscribe($actor_ref))
|
||||
.await
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! publish {
|
||||
($message_bus:expr, $message:expr) => {
|
||||
publish!(stringify!($message_bus), $message_bus, $message)
|
||||
};
|
||||
($bus_name:expr, $message_bus:expr, $message:expr) => {{
|
||||
tracing::debug!(bus = $bus_name, msg = stringify!($message), "publish");
|
||||
$message_bus
|
||||
.tell(kameo::actor::pubsub::Publish($message))
|
||||
.await
|
||||
}};
|
||||
}
|
|
@ -63,6 +63,7 @@ newtype!(
|
|||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
kameo::Reply,
|
||||
"ID of a Nextcloud Label"
|
||||
);
|
||||
newtype!(
|
||||
|
|
Loading…
Reference in a new issue