use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::adapter::Adapter;
use crate::error::DactylError;
use crate::rows::{Row, Rows};
#[derive(Clone)]
pub struct NeonAdapter {
inner: Arc<Inner>,
}
struct Inner {
endpoint: String,
bearer: Option<String>,
client: reqwest::blocking::Client,
}
#[derive(Debug, Serialize)]
struct QueryRequest<'a> {
sql: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
params: Option<&'a serde_json::Value>,
optimize: bool,
write: bool,
}
#[derive(Debug, Deserialize, Serialize)]
struct QueryResponse {
#[serde(default)]
columns: Vec<String>,
#[serde(default)]
rows: Vec<serde_json::Value>,
}
impl NeonAdapter {
pub fn new(endpoint: &str, bearer: Option<String>, _transport: Option<String>) -> Self {
let client = reqwest::blocking::Client::builder()
.build()
.expect("reqwest blocking client");
Self {
inner: Arc::new(Inner {
endpoint: endpoint.trim_end_matches('/').to_string(),
bearer,
client,
}),
}
}
}
impl Adapter for NeonAdapter {
fn execute(
&self,
query: &str,
params: Option<&serde_json::Value>,
optimize: bool,
write: bool,
) -> Result<Rows, DactylError> {
let path = if write { "/write" } else { "/read" };
let url = format!("{}{}", self.inner.endpoint, path);
let req = QueryRequest {
sql: query,
params,
optimize,
write,
};
let mut rb = self.inner.client.post(&url).json(&req);
if let Some(b) = &self.inner.bearer {
rb = rb.bearer_auth(b);
}
let resp = rb
.send()
.map_err(|e| DactylError::Adapter(format!("neon send: {e}")))?;
let status = resp.status();
let body: QueryResponse = resp
.json()
.map_err(|e| DactylError::Adapter(format!("neon decode: {e}")))?;
if !status.is_success() {
return Err(DactylError::Adapter(format!(
"neon status {status}: {}",
serde_json::to_string(&body).unwrap_or_default()
)));
}
let mut out = Vec::with_capacity(body.rows.len());
for r in body.rows {
let obj = r
.as_object()
.ok_or_else(|| DactylError::Adapter("neon row is not a JSON object".into()))?;
if body.columns.is_empty() {
let cols: Vec<String> = obj.keys().cloned().collect();
let vals: Vec<serde_json::Value> = cols
.iter()
.map(|c| obj.get(c).cloned().unwrap_or(serde_json::Value::Null))
.collect();
out.push(Row {
columns: cols,
values: vals,
});
} else {
let vals: Vec<serde_json::Value> = body
.columns
.iter()
.map(|c| obj.get(c).cloned().unwrap_or(serde_json::Value::Null))
.collect();
out.push(Row {
columns: body.columns.clone(),
values: vals,
});
}
}
Ok(Rows(out))
}
}