First working rescheduling
This commit is contained in:
parent
adc6b7866a
commit
b25857f3af
7 changed files with 85 additions and 20 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -217,6 +217,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"forgejo-api",
|
"forgejo-api",
|
||||||
|
"jiff",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -713,28 +714,45 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff"
|
name = "jiff"
|
||||||
version = "0.2.20"
|
version = "0.2.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543"
|
checksum = "819b44bc7c87d9117eb522f14d46e918add69ff12713c475946b0a29363ed1c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff-static",
|
"jiff-static",
|
||||||
|
"jiff-tzdb-platform",
|
||||||
"log",
|
"log",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff-static"
|
name = "jiff-static"
|
||||||
version = "0.2.20"
|
version = "0.2.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5"
|
checksum = "470252db18ecc35fd766c0891b1e3ec6cbbcd62507e85276c01bf75d8e94d4a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jiff-tzdb"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jiff-tzdb-platform"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
|
||||||
|
dependencies = [
|
||||||
|
"jiff-tzdb",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.87"
|
version = "0.3.87"
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,4 @@ serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
tokio = { version = "1.45.0", features = ["full"] }
|
tokio = { version = "1.45.0", features = ["full"] }
|
||||||
time = "0.3.41"
|
time = "0.3.41"
|
||||||
|
jiff = "0.2.22"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::task;
|
use crate::task;
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use forgejo_api::structs::Issue;
|
use forgejo_api::structs::Issue;
|
||||||
|
|
@ -33,12 +34,12 @@ fn task_from_issue(issue: &Issue) -> anyhow::Result<task::Task> {
|
||||||
fn task_state_from_issue(issue: &Issue) -> anyhow::Result<task::State> {
|
fn task_state_from_issue(issue: &Issue) -> anyhow::Result<task::State> {
|
||||||
match issue.get_state()? {
|
match issue.get_state()? {
|
||||||
forgejo_api::structs::StateType::Open => Ok(task::State::Open {
|
forgejo_api::structs::StateType::Open => Ok(task::State::Open {
|
||||||
due: issue.due_date,
|
due: issue.due_date.map(util::time_to_jiff),
|
||||||
}),
|
}),
|
||||||
forgejo_api::structs::StateType::Closed => Ok(task::State::Completed {
|
forgejo_api::structs::StateType::Closed => Ok(task::State::Completed {
|
||||||
date: issue
|
date: util::time_to_jiff(issue
|
||||||
.closed_at
|
.closed_at
|
||||||
.context("Closed issue without a closed_at date")?,
|
.context("Closed issue without a closed_at date")?),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,19 +125,19 @@ async fn list_all_issues(ctx: &crate::Context) -> anyhow::Result<Vec<Issue>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait IssueExt {
|
trait IssueExt {
|
||||||
fn get_number(&self) -> anyhow::Result<u64>;
|
fn get_number(&self) -> anyhow::Result<u32>;
|
||||||
fn get_title(&self) -> anyhow::Result<String>;
|
fn get_title(&self) -> anyhow::Result<String>;
|
||||||
fn get_state(&self) -> anyhow::Result<forgejo_api::structs::StateType>;
|
fn get_state(&self) -> anyhow::Result<forgejo_api::structs::StateType>;
|
||||||
fn get_label_names(&self) -> anyhow::Result<Vec<String>>;
|
fn get_label_names(&self) -> anyhow::Result<Vec<String>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IssueExt for Issue {
|
impl IssueExt for Issue {
|
||||||
fn get_number(&self) -> anyhow::Result<u64> {
|
fn get_number(&self) -> anyhow::Result<u32> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.number
|
.number
|
||||||
.context("Missing issue number")?
|
.context("Missing issue number")?
|
||||||
.try_into()
|
.try_into()
|
||||||
.context("Failed converting issue number to u64")?)
|
.context("Failed converting issue number to u32")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_title(&self) -> anyhow::Result<String> {
|
fn get_title(&self) -> anyhow::Result<String> {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ mod collect;
|
||||||
mod ci_meta;
|
mod ci_meta;
|
||||||
mod context;
|
mod context;
|
||||||
mod scheduler;
|
mod scheduler;
|
||||||
|
mod util;
|
||||||
|
|
||||||
use context::Context;
|
use context::Context;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::task;
|
use crate::task;
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
|
|
||||||
pub async fn reschedule_recurring_tasks(
|
pub async fn reschedule_recurring_tasks(
|
||||||
ctx: &crate::Context,
|
ctx: &crate::Context,
|
||||||
|
|
@ -17,10 +20,10 @@ pub async fn reschedule_recurring_tasks(
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let due_date = next_due_date(*completed_date, recurring.interval);
|
let due_date = next_due_date(completed_date, recurring.interval);
|
||||||
|
|
||||||
log::info!("Rescheduling {task} for {due_date}...");
|
log::info!("Rescheduling {task} for {due_date}...");
|
||||||
reopen_issue_with_due_date(ctx, task, due_date).await?;
|
|
||||||
|
reopen_issue_with_due_date(ctx, task, &due_date).await?;
|
||||||
rescheduled += 1;
|
rescheduled += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,10 +34,41 @@ pub async fn reschedule_recurring_tasks(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_due_date(completed_date: time::OffsetDateTime, interval: task::RecurringInterval) -> time::OffsetDateTime {
|
fn next_due_date(completed_date: &jiff::Zoned, interval: task::RecurringInterval) -> jiff::Zoned {
|
||||||
todo!()
|
let span = match interval {
|
||||||
|
task::RecurringInterval::Months(m) => jiff::Span::new().months(m),
|
||||||
|
task::RecurringInterval::Weeks(w) => jiff::Span::new().weeks(w),
|
||||||
|
task::RecurringInterval::Days(d) => jiff::Span::new().days(d),
|
||||||
|
};
|
||||||
|
|
||||||
|
completed_date + span
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn reopen_issue_with_due_date(ctx: &crate::Context, task: &task::Task, due_date: time::OffsetDateTime) -> anyhow::Result<()> {
|
async fn reopen_issue_with_due_date(
|
||||||
todo!()
|
ctx: &crate::Context,
|
||||||
|
task: &task::Task,
|
||||||
|
due_date: &jiff::Zoned,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
ctx.forgejo
|
||||||
|
.issue_edit_issue(
|
||||||
|
&ctx.owner,
|
||||||
|
&ctx.repo,
|
||||||
|
task.issue_number.into(),
|
||||||
|
forgejo_api::structs::EditIssueOption {
|
||||||
|
due_date: Some(util::jiff_to_time(due_date)),
|
||||||
|
state: Some("open".to_owned()),
|
||||||
|
assignee: None,
|
||||||
|
assignees: None,
|
||||||
|
body: None,
|
||||||
|
milestone: None,
|
||||||
|
r#ref: None,
|
||||||
|
title: None,
|
||||||
|
unset_due_date: None,
|
||||||
|
updated_at: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("Failed reopening recurring issue")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
/// Issue Number for referencing the task
|
/// Issue Number for referencing the task
|
||||||
pub issue_number: u64,
|
pub issue_number: u32,
|
||||||
|
|
||||||
/// Human-readable summary of the task
|
/// Human-readable summary of the task
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|
@ -18,10 +18,10 @@ pub enum State {
|
||||||
/// The task is open and pending completion.
|
/// The task is open and pending completion.
|
||||||
///
|
///
|
||||||
/// An optional due date may be present.
|
/// An optional due date may be present.
|
||||||
Open { due: Option<time::OffsetDateTime> },
|
Open { due: Option<jiff::Zoned> },
|
||||||
|
|
||||||
/// The task has been completed at the specified time.
|
/// The task has been completed at the specified time.
|
||||||
Completed { date: time::OffsetDateTime },
|
Completed { date: jiff::Zoned },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
10
src/util.rs
Normal file
10
src/util.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
pub fn time_to_jiff(t: time::OffsetDateTime) -> jiff::Zoned {
|
||||||
|
let tz = jiff::tz::TimeZone::fixed(jiff::tz::offset(t.offset().whole_hours()));
|
||||||
|
|
||||||
|
jiff::Timestamp::new(t.unix_timestamp(), 0).unwrap()
|
||||||
|
.to_zoned(tz)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jiff_to_time(t: &jiff::Zoned) -> time::OffsetDateTime {
|
||||||
|
time::OffsetDateTime::from_unix_timestamp(t.timestamp().as_second()).unwrap()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue