wip
This commit is contained in:
@@ -45,6 +45,8 @@ xdg = { version = "2.5", optional = true }
|
||||
uuid = { version = "1.12", features = ["serde", "v4"] }
|
||||
redb = { version = "2.4", optional = true }
|
||||
bincode = { version = "1.3", optional = true }
|
||||
serde_json = { version = "1" }
|
||||
codee = { version = "0.3" }
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::codec::ron::RonCodec;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::models;
|
||||
use crate::pages;
|
||||
use leptos::prelude::*;
|
||||
use leptos_router::components::*;
|
||||
use leptos_router::path;
|
||||
use std::sync::Arc;
|
||||
use type_toppings::ResultExt;
|
||||
|
||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
use leptos_meta::MetaTags;
|
||||
@@ -60,7 +61,13 @@ pub fn Home() -> impl leptos::IntoView {
|
||||
|
||||
let action = Action::new(|()| async move {
|
||||
tracing::debug!("running action");
|
||||
let walls = get_walls().await.unwrap().into_inner();
|
||||
let walls = get_walls()
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
dbg!(e);
|
||||
})
|
||||
.expect("failed to get walls")
|
||||
.into_inner();
|
||||
let wall = walls.first();
|
||||
|
||||
if let Some(wall) = wall {
|
||||
@@ -81,7 +88,7 @@ pub fn Home() -> impl leptos::IntoView {
|
||||
|
||||
#[server]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn get_walls() -> Result<RonCodec<Vec<models::Wall>>, ServerFnError> {
|
||||
async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
||||
use redb::ReadableTable;
|
||||
|
||||
tracing::debug!("get walls");
|
||||
@@ -104,5 +111,5 @@ async fn get_walls() -> Result<RonCodec<Vec<models::Wall>>, ServerFnError> {
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(RonCodec::new(walls))
|
||||
Ok(RonEncoded::new(walls))
|
||||
}
|
||||
|
||||
@@ -1,59 +1,159 @@
|
||||
pub mod ron {
|
||||
//! Wrap T in RonCodec<T> that when serialized, always serializes to a [ron] string.
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RonCodec<T> {
|
||||
t: T,
|
||||
use codee::Decoder;
|
||||
use codee::Encoder;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use server_fn::ServerFnError;
|
||||
use server_fn::codec::Encoding;
|
||||
use server_fn::codec::FromReq;
|
||||
use server_fn::codec::FromRes;
|
||||
use server_fn::codec::IntoReq;
|
||||
use server_fn::codec::IntoRes;
|
||||
use server_fn::request::ClientReq;
|
||||
use server_fn::request::Req;
|
||||
use server_fn::response::ClientRes;
|
||||
use server_fn::response::Res;
|
||||
|
||||
pub struct Ron;
|
||||
|
||||
impl<T> Encoder<T> for Ron
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
type Encoded = String;
|
||||
type Error = ron::Error;
|
||||
|
||||
fn encode(val: &T) -> Result<Self::Encoded, Self::Error> {
|
||||
ron::to_string(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RonCodec<T> {
|
||||
impl<T> Decoder<T> for Ron
|
||||
where
|
||||
for<'de> T: Deserialize<'de>,
|
||||
{
|
||||
type Encoded = str;
|
||||
type Error = ron::error::SpannedError;
|
||||
|
||||
fn decode(val: &Self::Encoded) -> Result<T, Self::Error> {
|
||||
ron::from_str(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoding for Ron {
|
||||
const CONTENT_TYPE: &'static str = "application/ron";
|
||||
const METHOD: http::Method = http::Method::POST;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RonEncoded<T>(pub T);
|
||||
|
||||
impl<T> RonEncoded<T> {
|
||||
pub fn into_inner(self) -> T {
|
||||
self.t
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn new(t: T) -> Self {
|
||||
Self { t }
|
||||
Self(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for RonCodec<T> {
|
||||
impl<T> std::ops::Deref for RonEncoded<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.t
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> serde::Serialize for RonCodec<T>
|
||||
// impl<T> serde::Serialize for RonEncoded<T>
|
||||
// where
|
||||
// T: serde::Serialize,
|
||||
// {
|
||||
// #[tracing::instrument(skip_all, err)]
|
||||
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: serde::Serializer,
|
||||
// {
|
||||
// let serialized = ron::to_string(&self.0).map_err(serde::ser::Error::custom)?;
|
||||
// serializer.serialize_str(&serialized)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'de, T> serde::Deserialize<'de> for RonEncoded<T>
|
||||
// where
|
||||
// T: serde::de::DeserializeOwned + 'static,
|
||||
// {
|
||||
// #[tracing::instrument(skip_all, err)]
|
||||
// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
// where
|
||||
// D: serde::Deserializer<'de>,
|
||||
// {
|
||||
// let s = String::deserialize(deserializer)?;
|
||||
// let t: T = ron::from_str(&s).map_err(serde::de::Error::custom)?;
|
||||
// Ok(Self(t))
|
||||
// }
|
||||
// }
|
||||
|
||||
// IntoReq
|
||||
impl<T, Request, Err> IntoReq<Ron, Request, Err> for RonEncoded<T>
|
||||
where
|
||||
T: serde::Serialize,
|
||||
Request: ClientReq<Err>,
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let serialized = ron::to_string(&self.t).map_err(serde::ser::Error::custom)?;
|
||||
serializer.serialize_str(&serialized)
|
||||
fn into_req(self, path: &str, accepts: &str) -> Result<Request, ServerFnError<Err>> {
|
||||
let data = Ron::encode(&self.0).map_err(|e| ServerFnError::Serialization(e.to_string()))?;
|
||||
Request::try_new_post(path, Ron::CONTENT_TYPE, accepts, data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> serde::Deserialize<'de> for RonCodec<T>
|
||||
// FromReq
|
||||
impl<T, Request, Err> FromReq<Ron, Request, Err> for RonEncoded<T>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + 'static,
|
||||
Request: Req<Err> + Send,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let t: T = ron::from_str(&s).map_err(serde::de::Error::custom)?;
|
||||
Ok(Self { t })
|
||||
async fn from_req(req: Request) -> Result<Self, ServerFnError<Err>> {
|
||||
let data = req.try_into_string().await?;
|
||||
Ron::decode(&data).map(RonEncoded).map_err(|e| ServerFnError::Args(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
// IntoRes
|
||||
impl<CustErr, T, Response> IntoRes<Ron, Response, CustErr> for RonEncoded<T>
|
||||
where
|
||||
Response: Res<CustErr>,
|
||||
T: Serialize + Send,
|
||||
{
|
||||
async fn into_res(self) -> Result<Response, ServerFnError<CustErr>> {
|
||||
let data = Ron::encode(&self.0).map_err(|e| ServerFnError::Serialization(e.to_string()))?;
|
||||
Response::try_from_string(Ron::CONTENT_TYPE, data)
|
||||
}
|
||||
}
|
||||
|
||||
// FromRes
|
||||
impl<T, Response, Err> FromRes<Ron, Response, Err> for RonEncoded<T>
|
||||
where
|
||||
Response: ClientRes<Err> + Send,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
async fn from_res(res: Response) -> Result<Self, ServerFnError<Err>> {
|
||||
let data = res.try_into_string().await?;
|
||||
Ron::decode(&data)
|
||||
.map(RonEncoded)
|
||||
.map_err(|e| ServerFnError::Deserialization(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::RonCodec;
|
||||
use super::Ron;
|
||||
use super::RonEncoded;
|
||||
use codee::Decoder;
|
||||
use codee::Encoder;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -63,25 +163,36 @@ pub mod ron {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_ron_wrapper() {
|
||||
// let original = TestStruct {
|
||||
// name: "Test".to_string(),
|
||||
// value: 42,
|
||||
// };
|
||||
|
||||
// // Wrap in RonCodec
|
||||
// let wrapped = RonEncoded::new(original.clone());
|
||||
|
||||
// // Serialize
|
||||
// let serialized = serde_json::to_string(&wrapped).expect("Serialization failed");
|
||||
// println!("Serialized: {}", serialized);
|
||||
|
||||
// // Deserialize
|
||||
// let deserialized: RonEncoded<TestStruct> = serde_json::from_str(&serialized).expect("Deserialization failed");
|
||||
|
||||
// // Compare
|
||||
// assert_eq!(deserialized.into_inner(), original);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_ron_codec() {
|
||||
let original = TestStruct {
|
||||
name: "Test".to_string(),
|
||||
value: 42,
|
||||
};
|
||||
|
||||
// Wrap in RonCodec
|
||||
let wrapped = RonCodec::new(original.clone());
|
||||
|
||||
// Serialize
|
||||
let serialized = serde_json::to_string(&wrapped).expect("Serialization failed");
|
||||
println!("Serialized: {}", serialized);
|
||||
|
||||
// Deserialize
|
||||
let deserialized: RonCodec<TestStruct> = serde_json::from_str(&serialized).expect("Deserialization failed");
|
||||
|
||||
// Compare
|
||||
assert_eq!(deserialized.into_inner(), original);
|
||||
let enc = Ron::encode(&original).unwrap();
|
||||
let dec: TestStruct = Ron::decode(&enc).unwrap();
|
||||
assert_eq!(dec, original);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::codec::ron::RonCodec;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::components::header::HeaderItem;
|
||||
use crate::components::header::HeaderItems;
|
||||
use crate::components::header::StyledHeader;
|
||||
@@ -150,7 +150,7 @@ pub struct Image {
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn load_initial_data() -> Result<RonCodec<InitialData>, ServerFnError> {
|
||||
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
||||
todo!()
|
||||
// let wall = state.persistent.with(|s| s.wall.clone()).await;
|
||||
// Ok(RonCodec::new(InitialData { wall }))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::codec::ron::RonCodec;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::components::header::HeaderItem;
|
||||
use crate::components::header::HeaderItems;
|
||||
use crate::components::header::StyledHeader;
|
||||
@@ -63,7 +63,7 @@ pub struct InitialData {
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn load_initial_data() -> Result<RonCodec<InitialData>, ServerFnError> {
|
||||
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
||||
todo!()
|
||||
// let state = expect_context::<State>();
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::codec::ron::RonCodec;
|
||||
use crate::codec::ron::Ron;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::components::button::Button;
|
||||
use crate::components::header::HeaderItem;
|
||||
use crate::components::header::HeaderItems;
|
||||
use crate::components::header::StyledHeader;
|
||||
use crate::models;
|
||||
use crate::models::HoldRole;
|
||||
use crate::models::WallUid;
|
||||
use leptos::Params;
|
||||
use leptos::prelude::*;
|
||||
use leptos::reactive::graph::ReactiveNode;
|
||||
@@ -17,21 +19,37 @@ struct WallParams {
|
||||
}
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn Wall() -> impl leptos::IntoView {
|
||||
tracing::debug!("Enter");
|
||||
let params = leptos_router::hooks::use_params::<WallParams>();
|
||||
|
||||
let wall = Resource::new(
|
||||
// TODO
|
||||
let wall = Resource::<models::Wall, Ron>::new_with_options(
|
||||
move || params.get().unwrap().id,
|
||||
move |wall_id| async move {
|
||||
if let Some(wall_id) = wall_id {
|
||||
let wall = get_wall(wall_id).await.unwrap().into_inner();
|
||||
Some(wall)
|
||||
} else {
|
||||
None
|
||||
{
|
||||
move |wall_uid: Option<WallUid>| async move {
|
||||
let wall_uid = wall_uid.unwrap();
|
||||
let wall = get_wall(wall_uid).await.unwrap().into_inner();
|
||||
wall
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
// // TODO
|
||||
// let wall = Resource::new(
|
||||
// move || params.get().unwrap().id,
|
||||
// move |wall_id| async move {
|
||||
// if let Some(wall_id) = wall_id {
|
||||
// let wall = get_wall(wall_id).await.unwrap();
|
||||
// Some(wall)
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
|
||||
let header_items = HeaderItems {
|
||||
left: vec![],
|
||||
middle: vec![HeaderItem {
|
||||
@@ -57,10 +75,10 @@ pub fn Wall() -> impl leptos::IntoView {
|
||||
<div class="m-2">
|
||||
<Suspense fallback=move || view! {<p>"Loading..."</p>}>
|
||||
{move || Suspend::new(async move{
|
||||
let wall: Option<Option<models::Wall>> = wall.get();
|
||||
let wall: Option<Option<RonEncoded<models::Wall>>> = wall.get();
|
||||
wall.map(|wall|{let wall = wall.unwrap();
|
||||
view! {
|
||||
<Ready wall />
|
||||
<Ready wall=wall.into_inner() />
|
||||
}
|
||||
})
|
||||
})}
|
||||
@@ -95,6 +113,7 @@ fn Ready(wall: models::Wall) -> impl leptos::IntoView {
|
||||
|
||||
let grid_classes = format!("grid grid-rows-{} grid-cols-{} gap-3", wall.rows, wall.cols);
|
||||
|
||||
tracing::debug!("view");
|
||||
view! {
|
||||
<div class="grid grid-cols-[auto,1fr] gap-8">
|
||||
// Render the wall
|
||||
@@ -114,7 +133,9 @@ fn Ready(wall: models::Wall) -> impl leptos::IntoView {
|
||||
}
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> impl leptos::IntoView {
|
||||
tracing::trace!("Enter");
|
||||
let class = move || {
|
||||
let role_classes = match role.get() {
|
||||
Some(HoldRole::Start) => Some("outline outline-offset-2 outline-green-500"),
|
||||
@@ -137,13 +158,18 @@ fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> imp
|
||||
view! { <img class="object-cover w-full h-full" src=src /> }
|
||||
});
|
||||
|
||||
tracing::trace!("view");
|
||||
view! { <div class=class>{img}</div> }
|
||||
}
|
||||
|
||||
#[server]
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn get_wall(wall_id: models::WallUid) -> Result<RonCodec<models::Wall>, ServerFnError> {
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, ServerFnError> {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
||||
enum Error {
|
||||
@@ -151,23 +177,43 @@ async fn get_wall(wall_id: models::WallUid) -> Result<RonCodec<models::Wall>, Se
|
||||
NotFound(#[error(not(source))] models::WallUid),
|
||||
}
|
||||
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
|
||||
let wall = tokio::task::spawn_blocking(move || -> Result<models::Wall, ServerFnError> {
|
||||
let read_txn = db.begin_read()?;
|
||||
|
||||
let walls_table = read_txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
||||
let wall = walls_table.get(wall_id)?.ok_or(Error::NotFound(wall_id))?.value();
|
||||
let wall = walls_table.get(wall_uid)?.ok_or(Error::NotFound(wall_uid))?.value();
|
||||
|
||||
Ok(wall)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(RonCodec::new(wall))
|
||||
tracing::debug!("ok");
|
||||
|
||||
Ok(RonEncoded::new(wall))
|
||||
}
|
||||
|
||||
#[server]
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn get_random_problem() -> Result<RonCodec<Option<models::Problem>>, ServerFnError> {
|
||||
Ok(RonCodec::new(None))
|
||||
async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
|
||||
let problem = tokio::task::spawn_blocking(move || -> Result<Option<models::Problem>, ServerFnError> {
|
||||
let read_txn = db.begin_read()?;
|
||||
|
||||
let table = read_txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
||||
let problem = table.get((wall_uid, problem_uid))?.map(|guard| guard.value());
|
||||
|
||||
Ok(problem)
|
||||
})
|
||||
.await??;
|
||||
|
||||
// use rand::seq::IteratorRandom;
|
||||
|
||||
@@ -184,5 +230,5 @@ async fn get_random_problem() -> Result<RonCodec<Option<models::Problem>>, Serve
|
||||
|
||||
// tracing::debug!("Returning randomized problem: {problem:?}");
|
||||
|
||||
// Ok(RonCodec::new(problem))
|
||||
Ok(RonEncoded::new(problem))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user