From f882cf2c953b46f8a20d5ccae322ba60e2428aa3 Mon Sep 17 00:00:00 2001 From: YetAnotherMinion Date: Thu, 13 Jan 2022 02:44:02 +0000 Subject: [PATCH] feat: starmelon compile Astrid.Pages.Route to html Automatically convert SQL rows to JSON if the user does not manually call json_object in the query themselves. --- src/exec/astrid_pages.rs | 105 ++++++++++++++++++-- src/exec/fixtures/astrid_pages.rs | 4 +- src/exec/fixtures/sql-client-integration.js | 14 ++- 3 files changed, 103 insertions(+), 20 deletions(-) diff --git a/src/exec/astrid_pages.rs b/src/exec/astrid_pages.rs index 8dec1eb..cf2cfd1 100644 --- a/src/exec/astrid_pages.rs +++ b/src/exec/astrid_pages.rs @@ -340,11 +340,35 @@ pub(crate) fn run( match stream.next().await { None => break, Some(Ok(row)) => { - match row.try_get::(0) { - Ok(s) => acc.push(s), - // TODO set an error flag before returning this one - Err(_) => break, - }; + use sqlx::ValueRef; + // If we only have one column and it is string type, + // then we can try to parse it as json + if row.len() == 1 { + match row.try_get::(0) { + Ok(s) => acc.push(s), + // TODO set an error flag before returning this one + Err(err) => { + failure = Some(AstridQueryError::Execute { + sql: sql.clone(), + message: err.to_string(), + }); + break; + } + }; + } else { + // Try to auto marshall the row into a javascript + // object to make it easier on our no-code users. + match try_marshal(&row) { + Ok(json) => acc.push(json.to_string()), + Err(err) => { + failure = Some(AstridQueryError::Execute { + sql: sql.clone(), + message: err.to_string(), + }); + break; + } + } + } } Some(Err(err)) => { eprintln!("got fetch_all sql error {:?}", err); @@ -358,11 +382,13 @@ pub(crate) fn run( } result.push(acc); } else { - match sqlx::query(&sql) - .fetch_one(&db_pool) - .await - .and_then(|row| row.try_get::(0)) - { + match sqlx::query(&sql).fetch_one(&db_pool).await.and_then(|row| { + if row.len() == 1 { + row.try_get::(0) + } else { + try_marshal(&row).map(|json| json.to_string()) + } + }) { Ok(s) => result.push(vec![s]), Err(sqlx::Error::RowNotFound) => { failure = Some(AstridQueryError::NotFound { sql }); @@ -480,3 +506,62 @@ enum AstridQueryError { sql: String, }, } + +fn try_marshal(row: &sqlx::sqlite::SqliteRow) -> Result { + use serde_json::value::{Map, Number}; + use serde_json::Value; + use sqlx::{Column, TypeInfo}; + + let mut object = Map::new(); + for i in 0..row.len() { + let column = row.column(i); + let value = match column.type_info().name() { + "NULL" => Value::Null, + "TEXT" => Value::String(row.try_get::(i)?), + "REAL" => { + let x = row.try_get::(i)?; + match Number::from_f64(x) { + Some(n) => Value::Number(n), + None => { + return Err(sqlx::Error::ColumnDecode { + index: column.name().to_owned(), + source: Box::new(StringError(format!("While parsing a SQL type `REAL` I expected a finite number but got {} instead ", x))), + }); + } + } + } + //"BLOB" => + "INTEGER" => Value::Number(row.try_get::(i)?.into()), + //"NUMERIC" => + "BOOLEAN" => Value::Bool(row.try_get::(i)?), + //"DATE" => + //"TIME" => + //"DATETIME" => + unknown => { + return Err(sqlx::Error::ColumnDecode { + index: column.name().to_owned(), + source: Box::new(StringError(format!("I don't know how to automatically convert the SQL type `{}`` into a JSON value.", unknown))), + }); + } + }; + object.insert(column.name().to_owned(), value); + } + + Ok(Value::Object(object)) +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StringError(String); + +impl ::std::fmt::Display for StringError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for StringError { + #[inline] + fn description(&self) -> &str { + &self.0 + } +} diff --git a/src/exec/fixtures/astrid_pages.rs b/src/exec/fixtures/astrid_pages.rs index d2c8001..6409e32 100644 --- a/src/exec/fixtures/astrid_pages.rs +++ b/src/exec/fixtures/astrid_pages.rs @@ -49,7 +49,7 @@ pub(crate) fn generate( #(" ")evalRoute (onStringOutput << encodeJson) flags #(target_module).route } OutputType::Bytes => { - #(" ")evalRoute (onStringOutput << encodeBytes) flags #(target_module).route + #(" ")evalRoute (onBytesOutput << encodeBytes) flags #(target_module).route } OutputType::Html => { #(" ")evalRoute (onStringOutput << encodeHtml) flags #(target_module).route @@ -160,7 +160,7 @@ pub(crate) fn generate( encodeSuccess : E.Value -> E.Value encodeSuccess value = E.object - [ ("ctor", E.string "Ok") + [ ("$", E.string "Ok") , ("a", value) ] diff --git a/src/exec/fixtures/sql-client-integration.js b/src/exec/fixtures/sql-client-integration.js index 78988c5..d61ff2f 100644 --- a/src/exec/fixtures/sql-client-integration.js +++ b/src/exec/fixtures/sql-client-integration.js @@ -24,10 +24,10 @@ function __Debug_assert(expr) { function _Query_succeed(value) { - return { - $: 0, - a: value - }; + return { + $: 0, + a: value + }; }; @@ -145,15 +145,12 @@ function _Query_runDecoder(decoder, sql, xs) case 1: if (xs.length === 0) { - __Debug_assert("did not find any results"); return $elm$core$Result$Err($author$project$Astrid$Query$NotFound(sql)); } var result = _Json_runOnString.f(decoder.a, xs[0]); if (!$elm$core$Result$isOk(result)) { - __Debug_assert("did not find any results for fetch One"); - __Debug_assert(sql); return $elm$core$Result$Err( A3( $author$project$Astrid$Query$Decode, @@ -203,7 +200,8 @@ function _Query_runDecoder(decoder, sql, xs) ); } } - return $elm$core$Result$Ok(_Json_toElmArray(array)); + + return $elm$core$Result$Ok(_Json_toElmArray(array)); } }