renovate 0.2.23

A new way to handle Postgres schema migration.
Documentation
use super::SinglePriv;
use crate::{
    parser::{utils::parsec::parse_single_priv, Privilege},
    DeltaItem, NodeItem,
};
use itertools::Itertools;
use pg_query::{protobuf::AccessPriv, Node, NodeEnum};
use std::{collections::BTreeSet, fmt, str::FromStr};

impl FromStr for SinglePriv {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> anyhow::Result<Self> {
        let (_, p) =
            parse_single_priv(s).map_err(|_| anyhow::anyhow!("invalid single priv: {}", s))?;
        Ok(p)
    }
}

impl From<SinglePriv> for AccessPriv {
    fn from(p: SinglePriv) -> Self {
        let cols = p
            .cols
            .into_iter()
            .map(|s| NodeEnum::String(pg_query::protobuf::String { str: s }))
            .map(|n| Node { node: Some(n) })
            .collect::<Vec<_>>();
        AccessPriv {
            priv_name: p.name,
            cols,
        }
    }
}

impl SinglePriv {
    fn generate_change(self, item: &Privilege, is_grant: bool) -> anyhow::Result<NodeEnum> {
        let mut stmt = item.inner()?.clone();
        stmt.is_grant = is_grant;
        stmt.privileges = vec![self.into()];
        Ok(NodeEnum::GrantStmt(stmt))
    }
}

impl DeltaItem for SinglePriv {
    type SqlNode = Privilege;
    fn drop(self, item: &Self::SqlNode) -> anyhow::Result<Vec<String>> {
        let node = self.generate_change(item, false)?;
        Ok(vec![node.deparse()?])
    }

    fn create(self, item: &Self::SqlNode) -> anyhow::Result<Vec<String>> {
        let node = self.generate_change(item, true)?;
        Ok(vec![node.deparse()?])
    }

    fn rename(self, _item: &Self::SqlNode, _new: Self) -> anyhow::Result<Vec<String>> {
        Ok(vec![])
    }

    fn alter(self, item: &Self::SqlNode, new: Self) -> anyhow::Result<Vec<String>> {
        let mut migrations = vec![];
        let sql = self.drop(item)?;
        migrations.extend(sql);
        let sql = new.create(item)?;
        migrations.extend(sql);
        Ok(migrations)
    }
}

impl From<SinglePriv> for Node {
    fn from(p: SinglePriv) -> Self {
        Node {
            node: Some(NodeEnum::AccessPriv(p.into())),
        }
    }
}

impl From<AccessPriv> for SinglePriv {
    fn from(p: AccessPriv) -> Self {
        let name = p.priv_name;
        let cols: BTreeSet<String> = p
            .cols
            .into_iter()
            .filter_map(|n| {
                n.node.and_then(|c| match c {
                    NodeEnum::String(s) => Some(s.str),
                    _ => None,
                })
            })
            .collect();
        Self { name, cols }
    }
}

impl fmt::Display for SinglePriv {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.name)?;
        if !self.cols.is_empty() {
            write!(f, "({})", self.cols.iter().join(", "))?;
        }
        Ok(())
    }
}