projkt 0.2.1

Generate project related stuffs such as .gitignore, license, readme and more
use std::{borrow::Cow, fs::OpenOptions, io::Write, path::PathBuf, sync::Arc};

use skim::{
    prelude::{unbounded, SkimOptionsBuilder},
    CaseMatching, ItemPreview, PreviewContext, Skim, SkimItem, SkimItemReceiver, SkimItemSender,
};

use crate::plugins::types::ProjktResult;

pub struct FuzzyItemType(pub String, pub String);

impl SkimItem for FuzzyItemType {
    fn text(&self) -> Cow<str> {
        Cow::Owned(self.0.to_string())
    }

    fn preview(&self, _: PreviewContext) -> ItemPreview {
        ItemPreview::Text(self.1.to_string())
    }

    fn output(&self) -> Cow<str> {
        Cow::Borrowed(&self.1)
    }
}

pub fn fuzzy(it: Vec<FuzzyItemType>, multi: bool) -> ProjktResult<Vec<Arc<dyn SkimItem>>> {
    let skim_options = SkimOptionsBuilder::default()
        .case(CaseMatching::Smart)
        .multi(multi)
        .preview_window(Some("right:80%"))
        .preview(Some(""))
        .reverse(true)
        .header(Some("Select any available templates"))
        .build()?;

    let (tx, rx): (SkimItemSender, SkimItemReceiver) = unbounded();

    for item in it {
        tx.send(Arc::new(item))?;
    }

    drop(tx);

    if let Some(out) = Skim::run_with(&skim_options, Some(rx)) {
        if out.is_abort {
            return Err("Aborted by user".into());
        }

        return Ok(out.selected_items);
    }

    Ok(vec![])
}

pub fn prompt(msg: String) -> ProjktResult<bool> {
    use dialoguer::{theme::ColorfulTheme, Confirm};

    let ans = Confirm::with_theme(&ColorfulTheme::default())
        .with_prompt(msg)
        .interact()?;

    Ok(ans)
}

pub fn write_or_create(
    file_opts: &mut OpenOptions,
    path: PathBuf,
    contents: &[u8],
    overwrite: bool,
) -> ProjktResult<bool> {
    let state = if path.exists() { "overwrite" } else { "create" };

    let display = if path.exists() {
        path.canonicalize()?
    } else {
        #[allow(clippy::redundant_clone)]
        path.clone()
    };

    if !overwrite && !prompt(format!("{state} {display:?}"))? {
        return Ok(false);
    }

    let mut file = file_opts.open(&path)?;

    file.write_all(contents)?;

    file.flush()?;

    Ok(true)
}