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.
This commit is contained in:
parent
cc4c1cf9d5
commit
f882cf2c95
3 changed files with 103 additions and 20 deletions
|
|
@ -340,11 +340,35 @@ pub(crate) fn run(
|
|||
match stream.next().await {
|
||||
None => break,
|
||||
Some(Ok(row)) => {
|
||||
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::<String, _>(0) {
|
||||
Ok(s) => acc.push(s),
|
||||
// TODO set an error flag before returning this one
|
||||
Err(_) => break,
|
||||
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::<String, _>(0))
|
||||
{
|
||||
match sqlx::query(&sql).fetch_one(&db_pool).await.and_then(|row| {
|
||||
if row.len() == 1 {
|
||||
row.try_get::<String, _>(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<serde_json::Value, sqlx::Error> {
|
||||
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::<String, _>(i)?),
|
||||
"REAL" => {
|
||||
let x = row.try_get::<f64, _>(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::<i64, _>(i)?.into()),
|
||||
//"NUMERIC" =>
|
||||
"BOOLEAN" => Value::Bool(row.try_get::<bool, _>(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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -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,6 +200,7 @@ function _Query_runDecoder(decoder, sql, xs)
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $elm$core$Result$Ok(_Json_toElmArray(array));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue