modularize date, database, and model code

- Add reusable date parsing, formatting, and recurrence modules
- Split database import helpers and model types into focused modules
- Expose the application as a library for the binary
This commit is contained in:
Chili Palmer
2026-08-19 22:09:40 +02:00
parent a6b38af396
commit c8ad487ca1
17 changed files with 731 additions and 665 deletions

View File

@@ -1,254 +1,16 @@
use std::{fmt, str::FromStr};
mod category;
mod enums;
mod item;
mod macro_def;
mod settings;
mod view;
#[derive(Debug, Clone, PartialEq)]
pub struct Item {
pub id: i64,
pub text: String,
pub note: String,
pub priority: i64,
pub when_at: Option<String>,
pub done_at: Option<String>,
pub alarm_at: Option<String>,
pub numeric_value: Option<f64>,
pub recurrence: String,
pub created_at: String,
pub updated_at: String,
pub discarded: bool,
pub categories: Vec<Category>,
}
impl Item {
pub fn category_names(&self) -> String {
self.categories
.iter()
.map(|c| c.name.as_str())
.collect::<Vec<_>>()
.join(", ")
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Category {
pub id: i64,
pub name: String,
pub parent_id: Option<i64>,
pub kind: CategoryKind,
pub match_text: String,
pub exclusive: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ViewDef {
pub id: i64,
pub name: String,
pub kind: ViewKind,
pub filter_value: String,
pub sort_key: SortKey,
pub show_done: bool,
pub filter_expr: String,
pub columns: Vec<ViewColumn>,
pub sections: Vec<ViewSection>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ViewColumn {
pub field: String,
pub heading: String,
pub width: u16,
pub aggregate: Aggregate,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ViewSection {
pub id: i64,
pub heading: String,
pub filter_expr: String,
pub collapsed: bool,
}
impl ViewDef {
pub fn columns_spec(&self) -> String {
self.columns
.iter()
.map(|c| format!("{}:{}:{}:{}", c.field, c.width, c.heading, c.aggregate))
.collect::<Vec<_>>()
.join(",")
}
pub fn sections_spec(&self) -> String {
self.sections
.iter()
.map(|s| {
format!(
"{}|{}{}",
s.heading,
s.filter_expr,
if s.collapsed { "|collapsed" } else { "" }
)
})
.collect::<Vec<_>>()
.join(";")
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CategoryRule {
pub id: i64,
pub category_id: i64,
pub condition_expr: String,
pub action_kind: RuleActionKind,
pub action_value: String,
pub enabled: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MacroDef {
pub id: i64,
pub name: String,
pub source: String,
pub key_binding: String,
}
#[derive(Debug, Clone, Default)]
pub struct ItemChanges {
pub text: Option<String>,
pub note: Option<String>,
pub priority: Option<i64>,
pub when_at: Option<Option<String>>,
pub alarm_at: Option<Option<String>>,
pub numeric_value: Option<Option<f64>>,
pub recurrence: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DocumentSettings {
pub description: String,
pub backup_on_open: bool,
pub trash_policy: TrashPolicy,
pub done_policy: DonePolicy,
pub automatic_filing: bool,
pub date_order: DateOrder,
pub week_start: WeekStart,
pub default_time: String,
pub morning_time: String,
pub afternoon_time: String,
pub evening_time: String,
pub note_tab_width: u8,
}
impl Default for DocumentSettings {
fn default() -> Self {
Self {
description: String::new(),
backup_on_open: false,
trash_policy: TrashPolicy::OnDemand,
done_policy: DonePolicy::Keep,
automatic_filing: true,
date_order: DateOrder::YearMonthDay,
week_start: WeekStart::Monday,
default_time: "09:00".into(),
morning_time: "09:00".into(),
afternoon_time: "13:00".into(),
evening_time: "18:00".into(),
note_tab_width: 4,
}
}
}
macro_rules! string_enum {
($name:ident { $($variant:ident => $value:literal),+ $(,)? }) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum $name {
$($variant),+
}
impl $name {
pub const fn as_str(self) -> &'static str {
match self {
$(Self::$variant => $value),+
}
}
}
impl fmt::Display for $name {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
impl FromStr for $name {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
$($value => Ok(Self::$variant),)+
_ => Err(format!("unsupported {} {value:?}", stringify!($name))),
}
}
}
};
}
string_enum!(CategoryKind {
Standard => "standard",
Date => "date",
Numeric => "numeric",
});
string_enum!(ViewKind {
List => "list",
Category => "category",
Upcoming => "upcoming",
Done => "done",
Datebook => "datebook",
Trash => "trash",
});
string_enum!(SortKey {
Manual => "manual",
When => "when",
Done => "done",
Priority => "priority",
Updated => "updated",
});
string_enum!(Aggregate {
None => "none",
Sum => "sum",
Average => "avg",
Count => "count",
Minimum => "min",
Maximum => "max",
});
string_enum!(RuleActionKind {
Assign => "assign",
Exclude => "exclude",
Priority => "priority",
Value => "value",
When => "when",
Alarm => "alarm",
Repeat => "repeat",
Done => "done",
});
string_enum!(TrashPolicy {
OnDemand => "on-demand",
OnClose => "on-close",
EndOfDay => "end-of-day",
Immediate => "immediate",
});
string_enum!(DonePolicy {
Keep => "keep",
Trash => "trash",
});
string_enum!(DateOrder {
YearMonthDay => "ymd",
MonthDayYear => "mdy",
DayMonthYear => "dmy",
});
string_enum!(WeekStart {
Monday => "monday",
Sunday => "sunday",
});
pub use category::{Category, CategoryRule};
pub use enums::{
Aggregate, CategoryKind, DateOrder, DonePolicy, RuleActionKind, SortKey, TrashPolicy, ViewKind,
WeekStart,
};
pub use item::{Item, ItemChanges};
pub use macro_def::MacroDef;
pub use settings::DocumentSettings;
pub use view::{ViewColumn, ViewDef, ViewSection};