First working rescheduling

This commit is contained in:
Rahix 2026-03-01 18:18:49 +01:00
parent adc6b7866a
commit b25857f3af
7 changed files with 85 additions and 20 deletions

View file

@ -1,4 +1,5 @@
use crate::task;
use crate::util;
use anyhow::Context as _;
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> {
match issue.get_state()? {
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 {
date: issue
date: util::time_to_jiff(issue
.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 {
fn get_number(&self) -> anyhow::Result<u64>;
fn get_number(&self) -> anyhow::Result<u32>;
fn get_title(&self) -> anyhow::Result<String>;
fn get_state(&self) -> anyhow::Result<forgejo_api::structs::StateType>;
fn get_label_names(&self) -> anyhow::Result<Vec<String>>;
}
impl IssueExt for Issue {
fn get_number(&self) -> anyhow::Result<u64> {
fn get_number(&self) -> anyhow::Result<u32> {
Ok(self
.number
.context("Missing issue number")?
.try_into()
.context("Failed converting issue number to u64")?)
.context("Failed converting issue number to u32")?)
}
fn get_title(&self) -> anyhow::Result<String> {

View file

@ -5,6 +5,7 @@ mod collect;
mod ci_meta;
mod context;
mod scheduler;
mod util;
use context::Context;

View file

@ -1,4 +1,7 @@
use crate::task;
use crate::util;
use anyhow::Context as _;
pub async fn reschedule_recurring_tasks(
ctx: &crate::Context,
@ -17,10 +20,10 @@ pub async fn reschedule_recurring_tasks(
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}...");
reopen_issue_with_due_date(ctx, task, due_date).await?;
reopen_issue_with_due_date(ctx, task, &due_date).await?;
rescheduled += 1;
}
@ -31,10 +34,41 @@ pub async fn reschedule_recurring_tasks(
Ok(())
}
fn next_due_date(completed_date: time::OffsetDateTime, interval: task::RecurringInterval) -> time::OffsetDateTime {
todo!()
fn next_due_date(completed_date: &jiff::Zoned, interval: task::RecurringInterval) -> jiff::Zoned {
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<()> {
todo!()
async fn reopen_issue_with_due_date(
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(())
}

View file

@ -1,7 +1,7 @@
#[derive(Debug, Clone)]
pub struct Task {
/// Issue Number for referencing the task
pub issue_number: u64,
pub issue_number: u32,
/// Human-readable summary of the task
pub title: String,
@ -18,10 +18,10 @@ pub enum State {
/// The task is open and pending completion.
///
/// 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.
Completed { date: time::OffsetDateTime },
Completed { date: jiff::Zoned },
}
#[derive(Debug, Clone)]

10
src/util.rs Normal file
View 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()
}