chore: cleanups and refactorings for cleaner code

This commit is contained in:
2026-07-18 21:39:16 +02:00
parent b438a99999
commit e5080b7282
63 changed files with 3454 additions and 4093 deletions

View File

@@ -2,7 +2,20 @@ use serde::{Deserialize, Serialize};
/// Tracks content hashes of generated files to skip unchanged writes.
/// Matches the `generated_file_hashes` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(
table_name = crate::db::schema::generated_file_hashes,
check_for_backend(diesel::sqlite::Sqlite)
)]
pub struct GeneratedFileHash {
pub project_id: String,
pub relative_path: String,
@@ -10,7 +23,10 @@ pub struct GeneratedFileHash {
pub updated_at: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "lowercase")]
pub enum NotificationEntity {
Post,
@@ -19,7 +35,10 @@ pub enum NotificationEntity {
Template,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "lowercase")]
pub enum NotificationAction {
Created,
@@ -27,14 +46,68 @@ pub enum NotificationAction {
Deleted,
}
impl std::str::FromStr for NotificationEntity {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"post" => Ok(Self::Post),
"media" => Ok(Self::Media),
"script" => Ok(Self::Script),
"template" => Ok(Self::Template),
_ => Err(format!("invalid NotificationEntity: {value}")),
}
}
}
impl NotificationEntity {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Post => "post",
Self::Media => "media",
Self::Script => "script",
Self::Template => "template",
}
}
}
impl std::str::FromStr for NotificationAction {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"created" => Ok(Self::Created),
"updated" => Ok(Self::Updated),
"deleted" => Ok(Self::Deleted),
_ => Err(format!("invalid NotificationAction: {value}")),
}
}
}
impl NotificationAction {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Created => "created",
Self::Updated => "updated",
Self::Deleted => "deleted",
}
}
}
/// Notification for CLI-to-app synchronization.
/// Matches the `db_notifications` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, diesel::Queryable, diesel::Selectable)]
#[diesel(
table_name = crate::db::schema::db_notifications,
check_for_backend(diesel::sqlite::Sqlite)
)]
pub struct DbNotification {
#[diesel(deserialize_as = i32)]
pub id: i64,
pub entity_type: NotificationEntity,
pub entity_id: String,
pub action: NotificationAction,
#[diesel(deserialize_as = crate::db::types::DbBool)]
pub from_cli: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub seen_at: Option<i64>,

View File

@@ -1,8 +1,20 @@
use diesel::ExpressionMethods;
use serde::{Deserialize, Serialize};
/// A media item (image, video, etc.).
/// Matches the `media` table schema.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::media, check_for_backend(diesel::sqlite::Sqlite))]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct Media {
pub id: String,
pub project_id: String,
@@ -30,6 +42,10 @@ pub struct Media {
pub checksum: Option<String>,
/// JSON-serialized string array in DB.
#[serde(default)]
#[diesel(
deserialize_as = crate::db::types::DbStringList,
serialize_as = crate::db::types::DbStringList
)]
pub tags: Vec<String>,
pub created_at: i64,
pub updated_at: i64,
@@ -37,7 +53,21 @@ pub struct Media {
/// A translation of media metadata into another language.
/// Matches the `media_translations` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(
table_name = crate::db::schema::media_translations,
check_for_backend(diesel::sqlite::Sqlite)
)]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct MediaTranslation {
pub id: String,
pub project_id: String,

View File

@@ -1,6 +1,10 @@
use diesel::ExpressionMethods;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "lowercase")]
pub enum PostStatus {
Draft,
@@ -9,17 +13,49 @@ pub enum PostStatus {
}
impl PostStatus {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Draft => "draft",
Self::Published => "published",
Self::Archived => "archived",
}
}
/// Returns true if this status is valid for a translation (draft or published only).
pub fn is_valid_for_translation(&self) -> bool {
matches!(self, PostStatus::Draft | PostStatus::Published)
}
}
impl std::str::FromStr for PostStatus {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"draft" => Ok(Self::Draft),
"published" => Ok(Self::Published),
"archived" => Ok(Self::Archived),
_ => Err(format!("invalid PostStatus: {value}")),
}
}
}
/// A blog post. Matches the `posts` table schema.
///
/// NOTE: `content` is null for published posts — body lives in the filesystem
/// `.md` file only. Draft content is stored in DB.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::posts, check_for_backend(diesel::sqlite::Sqlite))]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct Post {
pub id: String,
pub project_id: String,
@@ -36,6 +72,10 @@ pub struct Post {
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(default)]
#[diesel(
deserialize_as = crate::db::types::DbBool,
serialize_as = crate::db::types::DbBool
)]
pub do_not_translate: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub template_slug: Option<String>,
@@ -44,9 +84,17 @@ pub struct Post {
pub checksum: Option<String>,
/// JSON-serialized string array in DB.
#[serde(default)]
#[diesel(
deserialize_as = crate::db::types::DbStringList,
serialize_as = crate::db::types::DbStringList
)]
pub tags: Vec<String>,
/// JSON-serialized string array in DB.
#[serde(default)]
#[diesel(
deserialize_as = crate::db::types::DbStringList,
serialize_as = crate::db::types::DbStringList
)]
pub categories: Vec<String>,
// Published snapshot fields (used for diff detection)
#[serde(skip_serializing_if = "Option::is_none")]
@@ -68,7 +116,21 @@ pub struct Post {
/// A translation of a post into another language.
/// Matches the `post_translations` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(
table_name = crate::db::schema::post_translations,
check_for_backend(diesel::sqlite::Sqlite)
)]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct PostTranslation {
pub id: String,
pub project_id: String,
@@ -91,7 +153,10 @@ pub struct PostTranslation {
/// A link between two posts (tracked for backlinks).
/// Matches the `post_links` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug, Clone, Serialize, Deserialize, diesel::Queryable, diesel::Selectable, diesel::Insertable,
)]
#[diesel(table_name = crate::db::schema::post_links, check_for_backend(diesel::sqlite::Sqlite))]
pub struct PostLink {
pub id: String,
pub source_post_id: String,
@@ -103,7 +168,10 @@ pub struct PostLink {
/// Association between a post and media item.
/// Matches the `post_media` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug, Clone, Serialize, Deserialize, diesel::Queryable, diesel::Selectable, diesel::Insertable,
)]
#[diesel(table_name = crate::db::schema::post_media, check_for_backend(diesel::sqlite::Sqlite))]
pub struct PostMedia {
pub id: String,
pub project_id: String,

View File

@@ -1,7 +1,19 @@
use diesel::ExpressionMethods;
use serde::{Deserialize, Serialize};
/// A bDS project. Matches the `projects` table schema.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::projects, check_for_backend(diesel::sqlite::Sqlite))]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct Project {
pub id: String,
pub name: String,
@@ -10,13 +22,27 @@ pub struct Project {
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data_path: Option<String>,
#[diesel(
deserialize_as = crate::db::types::DbBool,
serialize_as = crate::db::types::DbBool
)]
pub is_active: bool,
pub created_at: i64,
pub updated_at: i64,
}
/// Key-value settings. Matches the `settings` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::settings, check_for_backend(diesel::sqlite::Sqlite))]
pub struct Setting {
pub key: String,
pub value: String,

View File

@@ -1,6 +1,10 @@
use diesel::ExpressionMethods;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "lowercase")]
pub enum ScriptKind {
Macro,
@@ -8,15 +12,73 @@ pub enum ScriptKind {
Transform,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "lowercase")]
pub enum ScriptStatus {
Draft,
Published,
}
impl ScriptKind {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Macro => "macro",
Self::Utility => "utility",
Self::Transform => "transform",
}
}
}
impl std::str::FromStr for ScriptKind {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"macro" => Ok(Self::Macro),
"utility" => Ok(Self::Utility),
"transform" => Ok(Self::Transform),
_ => Err(format!("invalid ScriptKind: {value}")),
}
}
}
impl ScriptStatus {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Draft => "draft",
Self::Published => "published",
}
}
}
impl std::str::FromStr for ScriptStatus {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"draft" => Ok(Self::Draft),
"published" => Ok(Self::Published),
_ => Err(format!("invalid ScriptStatus: {value}")),
}
}
}
/// A user-authored script. Matches the `scripts` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::scripts, check_for_backend(diesel::sqlite::Sqlite))]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct Script {
pub id: String,
pub project_id: String,
@@ -24,6 +86,10 @@ pub struct Script {
pub title: String,
pub kind: ScriptKind,
pub entrypoint: String,
#[diesel(
deserialize_as = crate::db::types::DbBool,
serialize_as = crate::db::types::DbBool
)]
pub enabled: bool,
pub version: i32,
pub file_path: String,

View File

@@ -1,6 +1,17 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::tags, check_for_backend(diesel::sqlite::Sqlite))]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct Tag {
pub id: String,
pub project_id: String,

View File

@@ -1,6 +1,10 @@
use diesel::ExpressionMethods;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "snake_case")]
pub enum TemplateKind {
Post,
@@ -9,21 +13,85 @@ pub enum TemplateKind {
Partial,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, diesel::AsExpression, diesel::FromSqlRow,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
#[serde(rename_all = "lowercase")]
pub enum TemplateStatus {
Draft,
Published,
}
impl TemplateKind {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Post => "post",
Self::List => "list",
Self::NotFound => "not_found",
Self::Partial => "partial",
}
}
}
impl std::str::FromStr for TemplateKind {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"post" => Ok(Self::Post),
"list" => Ok(Self::List),
"not_found" | "notFound" | "not-found" => Ok(Self::NotFound),
"partial" => Ok(Self::Partial),
_ => Err(format!("invalid TemplateKind: {value}")),
}
}
}
impl TemplateStatus {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Draft => "draft",
Self::Published => "published",
}
}
}
impl std::str::FromStr for TemplateStatus {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"draft" => Ok(Self::Draft),
"published" => Ok(Self::Published),
_ => Err(format!("invalid TemplateStatus: {value}")),
}
}
}
/// A Liquid template. Matches the `templates` table.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Serialize,
Deserialize,
diesel::Queryable,
diesel::Selectable,
diesel::Insertable,
diesel::AsChangeset,
)]
#[diesel(table_name = crate::db::schema::templates, check_for_backend(diesel::sqlite::Sqlite))]
#[diesel(treat_none_as_default_value = false, treat_none_as_null = true)]
pub struct Template {
pub id: String,
pub project_id: String,
pub slug: String,
pub title: String,
pub kind: TemplateKind,
#[diesel(
deserialize_as = crate::db::types::DbBool,
serialize_as = crate::db::types::DbBool
)]
pub enabled: bool,
pub version: i32,
pub file_path: String,