// yt - A fully featured command line YouTube client // // Copyright (C) 2025 Benedikt Peetz // SPDX-License-Identifier: GPL-3.0-or-later // // This file is part of Yt. // // You should have received a copy of the License along with this program. // If not, see . use std::mem; use crate::app::App; use anyhow::Result; use chrono::Utc; use log::{debug, trace}; use serde::{Serialize, de::DeserializeOwned}; use sqlx::{SqliteConnection, query}; pub(crate) mod maintenance; pub(crate) mod playlist; pub(crate) mod subscription; pub(crate) mod video; pub(crate) trait Committable: Sized + std::fmt::Debug + Serialize + DeserializeOwned { async fn commit(self, txn: &mut SqliteConnection) -> Result<()>; } #[derive(Debug)] pub(crate) struct Operations { name: &'static str, ops: Vec, } impl Default for Operations { fn default() -> Self { Self::new("") } } impl Operations { #[must_use] pub(crate) fn new(name: &'static str) -> Self { Self { name, ops: Vec::new(), } } pub(crate) async fn commit(mut self, app: &App) -> Result<()> { let ops = mem::take(&mut self.ops); if ops.is_empty() { return Ok(()); } trace!("Begin commit of {}", self.name); let mut txn = app.database.begin().await?; for op in ops { trace!("Commiting operation: {op:?}"); add_operation_to_txn_log(&op, &mut txn).await?; op.commit(&mut txn).await?; } txn.commit().await?; trace!("End commit of {}", self.name); Ok(()) } pub(crate) fn push(&mut self, op: O) { self.ops.push(op); } } impl Drop for Operations { fn drop(&mut self) { assert!( self.ops.is_empty(), "Trying to drop uncommitted operations (name: {}) ({:#?}). This is a bug.", self.name, self.ops ); } } async fn add_operation_to_txn_log( operation: &O, txn: &mut SqliteConnection, ) -> Result<()> { debug!("Adding operation to txn log: {operation:?}"); let now = Utc::now().timestamp(); let operation = serde_json::to_string(&operation).expect("should be serializable"); query!( r#" INSERT INTO txn_log ( timestamp, operation ) VALUES (?, ?); "#, now, operation, ) .execute(txn) .await?; Ok(()) }