pastor 0.14.0

The pastebin that hopefully doesn't suck
use std::{
    path::{Path, PathBuf},
    time::Duration,
};

use tracing::{debug, error};

use crate::file::{Paste, PasteError};

pub async fn cleanup_routine(storage: PathBuf) {
    let mut ticker = tokio::time::interval(Duration::from_secs(60));
    loop {
        ticker.tick().await;
        if let Err(err) = cleanup(&storage).await {
            error!("failure during cleanup: {err}");
        }
    }
}

pub async fn cleanup(storage: &Path) -> Result<(), PasteError> {
    for entry in std::fs::read_dir(storage)? {
        let path = entry?.path();
        match Paste::load_from_path(&path, None).await {
            Ok((paste, _)) => {
                if !paste.expired()? {
                    return Ok(());
                }

                debug!("Deleting: {paste:?}. (Now: {})", chrono::Utc::now().timestamp());
            }
            Err(err) => {
                error!("got error {err} while cleanup pastes, deleting path {path:?} anyways")
            }
        };

        tokio::fs::remove_file(path).await?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use std::path::Path;

    use tempfile::tempdir;
    use tracing_test::traced_test;

    use crate::{file::Paste, id::PasteId};

    async fn write_paste(storage: &Path, expires: i64) -> Paste {
        let now = chrono::Utc::now().timestamp();
        let paste = Paste {
            id: PasteId::new(),
            created: now,
            expires: now + expires,
            token: "secrettoken".to_string(),
            mime: "text/none".to_string(),
        };

        paste.write(storage).await.unwrap();

        paste
    }

    #[tokio::test]
    #[traced_test]
    async fn cleanup() {
        let dir = tempdir().unwrap();
        let storage = dir.path();
        let paste1 = write_paste(storage, -10).await;
        let paste2 = write_paste(storage, 60).await;

        crate::cleanup::cleanup(storage).await.unwrap();
        assert!(!paste1.path(storage).exists());
        assert!(paste2.path(storage).exists());
    }

    #[tokio::test]
    #[traced_test]
    async fn cleanup_broken_file() {
        let dir = tempdir().unwrap();
        let storage = dir.path();

        tokio::fs::write(storage.join("testfile"), "").await.unwrap();
        crate::cleanup::cleanup(storage).await.unwrap();
        assert!(!storage.join("testfile").exists());
    }
}