use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum JsonValue {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<JsonValue>),
Object(Vec<(String, JsonValue)>),
}
impl JsonValue {
pub fn null() -> Self {
JsonValue::Null
}
pub fn bool(b: bool) -> Self {
JsonValue::Bool(b)
}
pub fn number(n: impl Into<f64>) -> Self {
JsonValue::Number(n.into())
}
pub fn string(s: impl Into<String>) -> Self {
JsonValue::String(s.into())
}
pub fn object<I, K>(entries: I) -> Self
where
I: IntoIterator<Item = (K, JsonValue)>,
K: Into<String>,
{
JsonValue::Object(entries.into_iter().map(|(k, v)| (k.into(), v)).collect())
}
pub fn array<I>(items: I) -> Self
where
I: IntoIterator<Item = JsonValue>,
{
JsonValue::Array(items.into_iter().collect())
}
pub fn as_object(&self) -> Option<&[(String, JsonValue)]> {
match self {
JsonValue::Object(entries) => Some(entries.as_slice()),
_ => None,
}
}
pub fn to_json_string(&self) -> String {
let mut out = String::new();
write_json(self, &mut out);
out
}
}
fn write_json(value: &JsonValue, out: &mut String) {
match value {
JsonValue::Null => out.push_str("null"),
JsonValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
JsonValue::Number(n) => {
if n.fract() == 0.0 && n.is_finite() {
out.push_str(&format!("{}", *n as i64));
} else {
out.push_str(&format!("{n}"));
}
}
JsonValue::String(s) => {
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if (c as u32) < 0x20 => {
out.push_str(&format!("\\u{:04x}", c as u32));
}
c => out.push(c),
}
}
out.push('"');
}
JsonValue::Array(items) => {
out.push('[');
for (i, item) in items.iter().enumerate() {
if i > 0 {
out.push(',');
}
write_json(item, out);
}
out.push(']');
}
JsonValue::Object(entries) => {
out.push('{');
for (i, (k, v)) in entries.iter().enumerate() {
if i > 0 {
out.push(',');
}
write_json(&JsonValue::String(k.clone()), out);
out.push(':');
write_json(v, out);
}
out.push('}');
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValueOut {
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(String),
}
impl fmt::Display for ValueOut {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValueOut::Null => f.write_str("null"),
ValueOut::Bool(b) => write!(f, "{b}"),
ValueOut::Integer(n) => write!(f, "{n}"),
ValueOut::Float(n) => write!(f, "{n}"),
ValueOut::String(s) => write!(f, "{s}"),
}
}
}
#[derive(Debug, Clone)]
pub struct QueryResult {
pub statement: String,
pub affected: u64,
pub columns: Vec<String>,
pub rows: Vec<Vec<(String, ValueOut)>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct KvWatchEvent {
pub key: String,
pub op: String,
pub before: serde_json::Value,
pub after: serde_json::Value,
pub lsn: u64,
pub committed_at: u64,
pub dropped_event_count: u64,
}
#[cfg(any(feature = "redwire", feature = "http"))]
impl QueryResult {
pub fn from_envelope(value: serde_json::Value) -> Self {
let obj = value.as_object().cloned().unwrap_or_default();
let statement = obj
.get("statement")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let affected = obj.get("affected").and_then(|v| v.as_u64()).unwrap_or(0);
Self {
statement,
affected,
columns: Vec::new(),
rows: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct InsertResult {
pub affected: u64,
pub id: Option<String>,
}