247 lines
6.9 KiB
Rust
247 lines
6.9 KiB
Rust
use dever_runtime::api;
|
|
use dever_runtime::application::Session;
|
|
use dever_runtime::auth;
|
|
use dever_runtime::config::RuntimeProfile;
|
|
use dever_runtime::http::{Request, Response};
|
|
use dever_runtime::wire::decode::{self, Fields, Reader};
|
|
use dever_runtime::wire::{Encoded, Encoder};
|
|
use dever_runtime::{lifecycle, task};
|
|
|
|
#[derive(Default)]
|
|
struct Row {
|
|
id: i64,
|
|
text: String,
|
|
weight: f64,
|
|
note: Option<String>,
|
|
}
|
|
|
|
impl Fields for Row {
|
|
fn count(&self) -> usize {
|
|
4
|
|
}
|
|
|
|
fn index(&self, name: &str) -> Result<usize, String> {
|
|
match name {
|
|
"id" => Ok(0),
|
|
"text" => Ok(1),
|
|
"weight" => Ok(2),
|
|
"note" => Ok(3),
|
|
_ => Err("unknown wire record field".into()),
|
|
}
|
|
}
|
|
|
|
fn required(&self, index: usize) -> bool {
|
|
index != 3
|
|
}
|
|
|
|
fn read(&mut self, index: usize, reader: &mut Reader<'_, '_>) -> Result<(), String> {
|
|
match index {
|
|
0 => self.id = reader.int()?,
|
|
1 => self.text = reader.text()?.into_owned(),
|
|
2 => self.weight = reader.float()?,
|
|
3 => {
|
|
reader.nullable(&mut |reader| {
|
|
self.note = Some(reader.text()?.into_owned());
|
|
Ok(())
|
|
})?;
|
|
}
|
|
_ => return Err("invalid wire field index".into()),
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct Payload {
|
|
rows: Vec<Row>,
|
|
raw: Option<Encoded>,
|
|
}
|
|
|
|
impl Fields for Payload {
|
|
fn count(&self) -> usize {
|
|
2
|
|
}
|
|
|
|
fn index(&self, name: &str) -> Result<usize, String> {
|
|
match name {
|
|
"rows" => Ok(0),
|
|
"raw" => Ok(1),
|
|
_ => Err("unknown wire record field".into()),
|
|
}
|
|
}
|
|
|
|
fn required(&self, _: usize) -> bool {
|
|
true
|
|
}
|
|
|
|
fn read(&mut self, index: usize, reader: &mut Reader<'_, '_>) -> Result<(), String> {
|
|
match index {
|
|
0 => reader.list(&mut |reader| {
|
|
let mut row = Row::default();
|
|
reader.record(&mut row)?;
|
|
self.rows.push(row);
|
|
Ok(())
|
|
}),
|
|
1 => {
|
|
self.raw = Some(reader.json()?);
|
|
Ok(())
|
|
}
|
|
_ => Err("invalid wire field index".into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Payload {
|
|
fn encode(self) -> Result<Encoded, String> {
|
|
let mut encoder = Encoder::default();
|
|
encoder.compiled_record(2, |encoder| {
|
|
encoder.compiled_key("\"rows\":")?;
|
|
encoder.begin_array()?;
|
|
for row in self.rows {
|
|
encoder.compiled_record(4, |encoder| {
|
|
encoder.compiled_key("\"id\":")?;
|
|
encoder.int(row.id)?;
|
|
encoder.compiled_key(",\"text\":")?;
|
|
encoder.text(&row.text)?;
|
|
encoder.compiled_key(",\"weight\":")?;
|
|
encoder.float(row.weight)?;
|
|
encoder.compiled_key(",\"note\":")?;
|
|
match row.note {
|
|
Some(note) => encoder.text(¬e),
|
|
None => encoder.null(),
|
|
}
|
|
})?;
|
|
}
|
|
encoder.end()?;
|
|
encoder.compiled_key(",\"raw\":")?;
|
|
encoder.encoded(&self.raw.ok_or("missing required wire field")?)
|
|
})?;
|
|
encoder.finish()
|
|
}
|
|
}
|
|
|
|
enum Value {
|
|
Typed(Payload),
|
|
Raw(Encoded),
|
|
}
|
|
|
|
struct Arguments {
|
|
typed: bool,
|
|
value: Option<Value>,
|
|
}
|
|
|
|
impl Fields for Arguments {
|
|
fn count(&self) -> usize {
|
|
1
|
|
}
|
|
|
|
fn index(&self, name: &str) -> Result<usize, String> {
|
|
if name == if self.typed { "payload" } else { "source" } {
|
|
Ok(0)
|
|
} else {
|
|
Err("unknown wire record field".into())
|
|
}
|
|
}
|
|
|
|
fn required(&self, _: usize) -> bool {
|
|
true
|
|
}
|
|
|
|
fn read(&mut self, _: usize, reader: &mut Reader<'_, '_>) -> Result<(), String> {
|
|
self.value = Some(if self.typed {
|
|
let mut payload = Payload::default();
|
|
reader.record(&mut payload)?;
|
|
Value::Typed(payload)
|
|
} else {
|
|
Value::Raw(reader.json()?)
|
|
});
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
async fn call(mut request: Request, typed: bool) -> Response {
|
|
if let Err(error) = auth::require_components(&[], &[]).await {
|
|
return api::auth_error(error);
|
|
}
|
|
if let Err(error) = api::upload::read_body(&mut request).await {
|
|
return api::invalid_input(error);
|
|
}
|
|
let body = match api::Inputs::json_body(&request) {
|
|
Ok(body) => body,
|
|
Err(error) => return api::invalid_input(error),
|
|
};
|
|
let mut arguments = Arguments { typed, value: None };
|
|
if let Err(error) = decode::read(body, &mut |reader| reader.record(&mut arguments)) {
|
|
return api::invalid_input(api::InputError(error));
|
|
}
|
|
let encoded = match arguments.value {
|
|
Some(Value::Typed(payload)) => payload.encode(),
|
|
Some(Value::Raw(value)) => Ok(value),
|
|
None => Err("missing required wire field".into()),
|
|
};
|
|
match encoded {
|
|
Ok(value) => {
|
|
if api::commit_response_metadata().is_err() {
|
|
return api::internal_error();
|
|
}
|
|
api::success(value)
|
|
}
|
|
Err(_) => api::internal_error(),
|
|
}
|
|
}
|
|
|
|
async fn route(request: Request) -> Result<Response, String> {
|
|
let typed = match request.target.split('?').next().unwrap_or("") {
|
|
"/benchmark/echo/echo" => true,
|
|
"/benchmark/echo/raw" => false,
|
|
_ => return Ok(api::not_found()),
|
|
};
|
|
if request.method != "POST" {
|
|
return Ok(api::method_not_allowed(&["POST"]));
|
|
}
|
|
let prepared = match auth::prepare(&[], &request, true) {
|
|
Ok(prepared) => prepared,
|
|
Err(error) => return Ok(api::auth_error(error)),
|
|
};
|
|
let identity = prepared.claims().map(|claims| auth::Identity {
|
|
id: claims.subject.clone(),
|
|
user_id: None,
|
|
tenant_id: None,
|
|
});
|
|
Ok(
|
|
match auth::scope(prepared, identity, call(request, typed)).await {
|
|
Ok(response) => response,
|
|
Err(error) => api::auth_error(error),
|
|
},
|
|
)
|
|
}
|
|
|
|
fn main() -> Result<(), String> {
|
|
let session = Session::load(
|
|
RuntimeProfile {
|
|
sqlite: true,
|
|
postgres: false,
|
|
},
|
|
&[(None, "benchmark", false)],
|
|
&[],
|
|
)?;
|
|
session.initialize_authorization(false)?;
|
|
let cleanup = session.clone();
|
|
task::run_entry_with_typed_cleanup(
|
|
task::RuntimeConfig::default(),
|
|
session.clone().scope(async move {
|
|
session
|
|
.databases()
|
|
.prepare()
|
|
.await
|
|
.map_err(|error| error.to_string())?;
|
|
lifecycle::configure(true, false)?;
|
|
lifecycle::run(api::serve(route)).await
|
|
}),
|
|
|result| async move {
|
|
let closed = cleanup.close().await;
|
|
result.and(closed)
|
|
},
|
|
)
|
|
}
|