supabase-plus 0.7.2

An extra set of tools for managing Supabase projects going beyond the possibilities of regular Supabase CLI
use std::io::Stderr;

use anyhow::Context;
use promptuity::{
    Promptuity,
    prompts::{Confirm, Input, Select, SelectOption},
};

use crate::abstraction::Migration;

pub struct NewBucket {
    pub name: String,
    pub public: bool,
    pub mime_types: Vec<String>,
}

impl Migration for NewBucket {
    fn sql(&self) -> String {
        format!(
            r#"INSERT INTO storage.buckets (id, name, public, allowed_mime_types) VALUES ('{0}', '{0}', {1}, '{{{2}}}'::text[]);"#,
            self.name,
            self.public,
            self.mime_types.join(", ")
        )
    }

    fn migration_name(&self) -> String {
        format!("create_{}_bucket", self.name)
    }
}

impl NewBucket {
    pub fn new_interactively(
        promptuity: &mut Promptuity<'_, Stderr>,
        project_id: &str,
    ) -> anyhow::Result<Self> {
        let intro = format!("Creating bucket ({})", project_id);

        promptuity
            .with_intro(&intro)
            .begin()
            .expect("You don't seem to be in an interactive mode");

        let name = promptuity
            .prompt(
                Input::new("Please enter a slug for your new bucket").with_placeholder("cabinets"),
            )
            .ok()
            .context("Stopped")?;

        let public = promptuity
            .prompt(
                Select::new(
                    "Set the visibility of your bucket",
                    vec![
                        SelectOption::new("Public", true),
                        SelectOption::new("Private", false),
                    ],
                )
                .with_page_size(2),
            )
            .context("Stopped")?;

        let mime_type_limitation = promptuity
            .prompt(
                Confirm::new("Would you also like to limit accepted mime types?")
                    .with_default(false),
            )
            .context("Stopped")?;

        let mut mime_types: Vec<String> = vec![];

        if mime_type_limitation {
            loop {
                let hint = format!("{}", mime_types.clone().join(", "));

                let ext = promptuity
                    .prompt(
                        Input::new("Please enter a file extension and we will guess the mime type")
                            .with_placeholder("jpg")
                            .with_hint(&hint),
                    )
                    .context("Stopped")?;

                let Some(mime_type) = mime_guess::from_path(format!("_.{}", ext)).first() else {
                    continue;
                };

                mime_types.push(mime_type.to_string());

                let hint = format!("[{}]", mime_types.clone().join(", "));
                let will_add_more = promptuity
                    .prompt(
                        Confirm::new("Would you like to add another mime type?")
                            .with_default(false)
                            .with_hint(hint),
                    )
                    .context("Stopped")?;

                if !will_add_more {
                    break;
                }
            }
        }

        Ok(Self {
            name,
            public,
            mime_types,
        })
    }
}