pastor 0.14.0

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

use tracing::debug;

use crate::{dict::*, file::PasteError};

#[derive(Debug, Clone, PartialEq)]
pub struct PasteId {
    pub id: String,
    pub ext: Option<String>,
}

impl TryFrom<&Path> for PasteId {
    type Error = PasteError;

    fn try_from(path: &Path) -> Result<Self, Self::Error> {
        Ok(PasteId::from(
            path.file_name()
                .ok_or(PasteError::PasteIdFromPath(path.into()))?
                .to_str()
                .ok_or(PasteError::PasteIdFromPath(path.into()))?,
        ))
    }
}

impl From<&str> for PasteId {
    fn from(input: &str) -> Self {
        debug!("parsing id from: {input}");
        let (id, ext) = if input.contains('.') {
            let parts = input.split('.').collect::<Vec<&str>>();
            let id_without = parts[0].to_string();
            let ext = parts[1].to_string();
            (id_without, Some(ext))
        } else {
            (input.to_string(), None)
        };

        Self { id, ext }
    }
}

impl PasteId {
    pub fn new() -> Self {
        use rand::seq::SliceRandom;

        let mut rng = rand::thread_rng();
        let id = DICT_ADJ.choose(&mut rng).unwrap().to_string() + DICT_NOUN.choose(&mut rng).unwrap();

        Self { id, ext: None }
    }

    pub fn path(&self, root: &Path) -> std::path::PathBuf {
        root.join(self)
    }

    #[allow(unused)]
    pub fn ext(&self) -> &str {
        self.ext.as_ref().map_or("", |s| s.as_str())
    }

    #[allow(unused)]
    pub fn is_valid(&self) -> bool {
        self.id.chars().all(|c: char| c.is_ascii_lowercase()) && self.id.len() <= 128
    }
}

impl fmt::Display for PasteId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.id)?;
        if let Some(e) = &self.ext {
            f.write_str(".")?;
            f.write_str(e)?;
        };

        Ok(())
    }
}

impl AsRef<std::path::Path> for PasteId {
    fn as_ref(&self) -> &std::path::Path {
        std::path::Path::new(&self.id)
    }
}