use chrono::NaiveDateTime;
use paste::paste;
use serde::{Deserialize, Serialize};
use std::{fmt::Display, str::FromStr};
use crate::common::Precision;
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct DatabaseProperties {
pub vgroups: Option<u64>,
pub replica: Option<u16>,
pub quorum: Option<u16>,
pub days: Option<u16>,
pub keep: Option<String>,
#[serde(rename = "cache(MB)")]
pub cache: Option<u32>,
pub blocks: Option<u32>,
pub minrows: Option<u32>,
pub maxrows: Option<u32>,
#[serde(rename = "wallevel")]
pub wal: Option<u8>,
pub fsync: Option<u32>,
pub comp: Option<u8>,
pub cachelast: Option<u8>,
pub precision: Option<Precision>,
pub update: Option<u8>,
}
macro_rules! _prop_builder {
($($f:ident)*, $ty:ty) => {
$(pub fn $f(mut self, $f: $ty) -> Self {
self.$f = Some($f);
self
})*
};
}
impl DatabaseProperties {
pub fn new() -> Self {
Self::default()
}
_prop_builder!(vgroups, u64);
_prop_builder!(cache blocks minrows maxrows fsync, u32);
_prop_builder!(replica quorum days, u16);
_prop_builder!(wal comp cachelast update, u8);
_prop_builder!(precision, Precision);
_prop_builder!(keep, String);
}
impl Display for DatabaseProperties {
#[inline]
#[allow(unused_assignments)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut has_wrote = false;
macro_rules! _write_if {
($($f:ident) *) => {
$(if let Some($f) = &self.$f {
if has_wrote {
write!(f, " {} {}", paste!(stringify!([<$f:upper>])), $f)?;
} else {
write!(f, "{} {}", paste!(stringify!([<$f:upper>])), $f)?;
has_wrote = true;
}
})*
};
('str $($s:ident) *) => {
$(if let Some($s) = &self.$s {
if has_wrote {
write!(f, " {} '{}'", paste!(stringify!([<$s:upper>])), $s)?;
} else {
write!(f, "{} '{}'", paste!(stringify!([<$s:upper>])), $s)?;
has_wrote = true;
}
})*
};
($($f:ident) *; 'str $($s:ident) *) => {
_write_if!($($f) *);
_write_if!('str $($s) *)
};
($($f:ident) *; 'str $($s:ident) *; $($f2:ident) *) => {
_write_if!($($f) *; 'str $($s) *);
_write_if!($($f2) *)
};
}
_write_if!(vgroups replica quorum days keep cache blocks minrows
maxrows wal fsync comp cachelast; 'str precision; update);
Ok(())
}
}
impl FromStr for DatabaseProperties {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use nom::branch::alt;
use nom::character::complete::*;
use nom::character::streaming;
use nom::multi::many0;
use nom::sequence::*;
use nom::{bytes::complete::tag, IResult};
let mut repr = Self::new();
fn parse_name(s: &str) -> IResult<&str, &str> {
preceded(
tuple((multispace0, tag("CREATE DATABASE"), multispace1)),
alphanumeric1,
)(s)
}
let s = parse_name(s).map(|s| s.0).unwrap_or(s);
fn parse_props(s: &str) -> IResult<&str, Vec<(&str, &str)>> {
many0(separated_pair(
preceded(multispace0, alphanumeric1),
streaming::char(' '),
alt((
alphanumeric1,
delimited(char('\''), alphanumeric1, char('\'')),
)),
))(s)
}
if let Ok((_s, props)) = dbg!(parse_props(s)) {
for (prop, value) in props {
macro_rules! _parse {
($($($f:ident) +, $t:ident);*) => {
paste::paste! {
match prop.to_lowercase() {
$($(s if s == stringify!($f) => {
repr = repr.$f($t::from_str(value)?);
},)*)*
_ => (),
}
}
}
}
_parse!(vgroups, u64;
cache blocks minrows maxrows fsync, u32;
replica quorum days, u16;
wal comp cachelast update, u8;
keep, String;
precision, Precision);
}
Ok(repr)
} else {
Ok(repr)
}
}
}
#[test]
fn db_prop_from_str() {
let s = "REPLICA 1 QUORUM 1 DAYS 10 KEEP 30 CACHE 1 BLOCKS 3 MINROWS 100 MAXROWS 4096 WAL 1 FSYNC 3000 COMP 2 CACHELAST 0 PRECISION 'us' UPDATE 0";
let db = DatabaseProperties::from_str(s).unwrap();
let t = db.to_string();
dbg!(db);
assert_eq!(s, t);
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct DatabaseRepr {
pub name: String,
#[serde(flatten)]
pub props: DatabaseProperties,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShowDatabase {
pub name: String,
pub created_time: Option<NaiveDateTime>,
pub ntables: Option<usize>,
#[serde(flatten)]
pub props: DatabaseProperties,
pub status: Option<String>,
}
unsafe impl Send for ShowDatabase {}