Compare commits
2 Commits
b28546d2de
...
aebd30c1c2
| Author | SHA1 | Date | |
|---|---|---|---|
| aebd30c1c2 | |||
| bf8e79b88c |
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -156,7 +156,7 @@ dependencies = [
|
|||||||
"codee",
|
"codee",
|
||||||
"confik",
|
"confik",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"derive_more",
|
"derive_more 2.0.1",
|
||||||
"error_reporter",
|
"error_reporter",
|
||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
@@ -776,7 +776,16 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
|
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_more-impl",
|
"derive_more-impl 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl 2.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -791,6 +800,18 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -2113,7 +2134,7 @@ dependencies = [
|
|||||||
name = "moonboard-parser"
|
name = "moonboard-parser"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_more",
|
"derive_more 1.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ chrono = { version = "0.4.39", features = ["now", "serde"] }
|
|||||||
clap = { version = "4.5.7", features = ["derive"] }
|
clap = { version = "4.5.7", features = ["derive"] }
|
||||||
confik = { version = "0.12", optional = true, features = ["camino"] }
|
confik = { version = "0.12", optional = true, features = ["camino"] }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
derive_more = { version = "1", features = [
|
derive_more = { version = "2", features = [
|
||||||
"display",
|
"display",
|
||||||
"error",
|
"error",
|
||||||
"from",
|
"from",
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ pub use v2::WallUid;
|
|||||||
|
|
||||||
pub mod v2 {
|
pub mod v2 {
|
||||||
use super::v1;
|
use super::v1;
|
||||||
|
use derive_more::Display;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
@@ -78,10 +80,15 @@ pub mod v2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Display)]
|
||||||
pub enum Method {
|
pub enum Method {
|
||||||
|
#[display("Feet follow hands")]
|
||||||
FeetFollowHands,
|
FeetFollowHands,
|
||||||
|
|
||||||
|
#[display("Footless")]
|
||||||
Footless,
|
Footless,
|
||||||
|
|
||||||
|
#[display("Footless plus kickboard")]
|
||||||
FootlessPlusKickboard,
|
FootlessPlusKickboard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// TODO: Put current problem UID in url
|
||||||
|
|
||||||
use crate::codec::ron::RonEncoded;
|
use crate::codec::ron::RonEncoded;
|
||||||
use crate::components::button::Button;
|
use crate::components::button::Button;
|
||||||
use crate::components::header::HeaderItem;
|
use crate::components::header::HeaderItem;
|
||||||
@@ -38,7 +40,6 @@ pub fn Wall() -> impl IntoView {
|
|||||||
});
|
});
|
||||||
let problem_signal = Signal::derive(move || {
|
let problem_signal = Signal::derive(move || {
|
||||||
let v = problem_action.value().read_only().get();
|
let v = problem_action.value().read_only().get();
|
||||||
tracing::debug!("val: {:?}", v);
|
|
||||||
v.and_then(Result::ok)
|
v.and_then(Result::ok)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ pub fn Wall() -> impl IntoView {
|
|||||||
};
|
};
|
||||||
|
|
||||||
leptos::view! {
|
leptos::view! {
|
||||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
<div class="min-w-screen min-h-screen bg-neutral-950">
|
||||||
<StyledHeader items=Signal::derive(header_items) />
|
<StyledHeader items=Signal::derive(header_items) />
|
||||||
|
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
@@ -90,15 +91,6 @@ pub fn Wall() -> impl IntoView {
|
|||||||
<Grid wall=wall.clone() problem=problem_signal />
|
<Grid wall=wall.clone() problem=problem_signal />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
{move || problem_signal.get().map(|p| p.name.clone())}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{move || problem_signal.get().map(|p| p.set_by.clone())}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onclick=move |_| {
|
onclick=move |_| {
|
||||||
if let Some(problem_uid) = wall.random_problem() {
|
if let Some(problem_uid) = wall.random_problem() {
|
||||||
@@ -108,6 +100,10 @@ pub fn Wall() -> impl IntoView {
|
|||||||
}
|
}
|
||||||
text="➤ Next problem"
|
text="➤ Next problem"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="m-4"/>
|
||||||
|
|
||||||
|
{move || problem_signal.get().map(|problem| view! { <ProblemInfo problem /> })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
};
|
};
|
||||||
@@ -142,6 +138,7 @@ fn Grid(wall: models::Wall, problem: Signal<Option<models::Problem>>) -> impl In
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: refactor this to use the Problem component
|
||||||
#[component]
|
#[component]
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn Hold(hold: models::Hold, role: Signal<Option<HoldRole>>) -> impl IntoView {
|
fn Hold(hold: models::Hold, role: Signal<Option<HoldRole>>) -> impl IntoView {
|
||||||
@@ -171,3 +168,30 @@ fn Hold(hold: models::Hold, role: Signal<Option<HoldRole>>) -> impl IntoView {
|
|||||||
tracing::trace!("view");
|
tracing::trace!("view");
|
||||||
view! { <div class=class>{img}</div> }
|
view! { <div class=class>{img}</div> }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
fn ProblemInfo(problem: models::Problem) -> impl IntoView {
|
||||||
|
tracing::trace!("Enter problem info");
|
||||||
|
|
||||||
|
let name = problem.name;
|
||||||
|
let set_by = problem.set_by;
|
||||||
|
let method = problem.method;
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="grid grid-cols-[auto,1fr] gap-2">
|
||||||
|
<NameValue name="Name:" value=name />
|
||||||
|
<NameValue name="Method:" value=method.to_string() />
|
||||||
|
<NameValue name="Set By:" value=set_by />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
fn NameValue(#[prop(into)] name: String, #[prop(into)] value: String) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<p class="text-orange-300">{name}</p>
|
||||||
|
<p class="text-white">{value}</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ pub mod db;
|
|||||||
mod migrations;
|
mod migrations;
|
||||||
pub mod operations;
|
pub mod operations;
|
||||||
|
|
||||||
pub const STATE_FILE: &str = "datastore/private/state.ron";
|
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
use crate::server::cli::Cli;
|
use crate::server::cli::Cli;
|
||||||
@@ -50,8 +48,9 @@ async fn serve(cli: Cli) -> Result<(), Error> {
|
|||||||
use leptos_axum::generate_route_list;
|
use leptos_axum::generate_route_list;
|
||||||
|
|
||||||
tracing::debug!("Creating DB");
|
tracing::debug!("Creating DB");
|
||||||
let db = db::Database::create()?;
|
let db = db::Database::create().map_err(db::DatabaseOperationError::from)?;
|
||||||
|
|
||||||
|
db::init_at_current_version(&db).await?;
|
||||||
migrations::run_migrations(&db).await.map_err(Error::Migration)?;
|
migrations::run_migrations(&db).await.map_err(Error::Migration)?;
|
||||||
|
|
||||||
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
||||||
@@ -125,5 +124,5 @@ pub enum Error {
|
|||||||
|
|
||||||
Confik(confik::Error),
|
Confik(confik::Error),
|
||||||
|
|
||||||
Database(redb::Error),
|
Database(db::DatabaseOperationError),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use bincode::Bincode;
|
use bincode::Bincode;
|
||||||
use redb::ReadTransaction;
|
use redb::ReadTransaction;
|
||||||
|
use redb::ReadableTable;
|
||||||
|
use redb::ReadableTableMetadata;
|
||||||
use redb::TableDefinition;
|
use redb::TableDefinition;
|
||||||
use redb::WriteTransaction;
|
use redb::WriteTransaction;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -83,6 +86,46 @@ impl Version {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, err)]
|
||||||
|
pub async fn init_at_current_version(db: &Database) -> Result<(), DatabaseOperationError> {
|
||||||
|
db.write(|txn| {
|
||||||
|
let mut version_table = txn.open_table(TABLE_VERSION)?;
|
||||||
|
let is_missing_version = version_table.get(())?.is_none();
|
||||||
|
if is_missing_version {
|
||||||
|
let v = Version::current();
|
||||||
|
tracing::warn!("INITIALIZING DATABASE AT VERSION {v}");
|
||||||
|
version_table.insert((), v)?;
|
||||||
|
|
||||||
|
// Root table
|
||||||
|
{
|
||||||
|
let mut table = txn.open_table(current::TABLE_ROOT)?;
|
||||||
|
assert!(table.is_empty()?);
|
||||||
|
table.insert((), models::Root { walls: BTreeSet::new() })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walls table
|
||||||
|
{
|
||||||
|
// Opening the table creates the table
|
||||||
|
let table = txn.open_table(current::TABLE_WALLS)?;
|
||||||
|
assert!(table.is_empty()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Problems table
|
||||||
|
{
|
||||||
|
// Opening the table creates the table
|
||||||
|
let table = txn.open_table(current::TABLE_PROBLEMS)?;
|
||||||
|
assert!(table.is_empty()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::models;
|
||||||
pub use v2 as current;
|
pub use v2 as current;
|
||||||
|
|
||||||
pub mod v2 {
|
pub mod v2 {
|
||||||
|
|||||||
@@ -1,218 +1,6 @@
|
|||||||
use super::db;
|
|
||||||
use super::db::Database;
|
use super::db::Database;
|
||||||
use crate::models;
|
|
||||||
use image::ImageDecoder;
|
|
||||||
use redb::ReadableTable;
|
|
||||||
use redb::ReadableTableMetadata;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use type_toppings::ResultExt;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, err)]
|
#[tracing::instrument(skip_all, err)]
|
||||||
pub async fn run_migrations(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn run_migrations(_db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
migrate_from_ron_to_redb(db).await?;
|
|
||||||
init_at_current_version(db).await?;
|
|
||||||
migrate_to_v2(db).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use redb DB instead of Ron state file
|
|
||||||
#[tracing::instrument(skip_all, err)]
|
|
||||||
async fn migrate_from_ron_to_redb(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let ron_state_file_path = PathBuf::from(super::STATE_FILE);
|
|
||||||
|
|
||||||
if ron_state_file_path
|
|
||||||
.try_exists()
|
|
||||||
.expect_or_report_with(|| format!("Failed to read {}", ron_state_file_path.display()))
|
|
||||||
{
|
|
||||||
tracing::warn!("MIGRATING");
|
|
||||||
|
|
||||||
let ron_state: models::v1::PersistentState = {
|
|
||||||
let content = tokio::fs::read_to_string(&ron_state_file_path).await?;
|
|
||||||
ron::from_str(&content)?
|
|
||||||
};
|
|
||||||
|
|
||||||
db.write(|txn| {
|
|
||||||
let mut version_table = txn.open_table(db::TABLE_VERSION)?;
|
|
||||||
assert!(version_table.is_empty()?);
|
|
||||||
version_table.insert((), db::Version { version: 1 })?;
|
|
||||||
|
|
||||||
let mut root_table = txn.open_table(db::v1::TABLE_ROOT)?;
|
|
||||||
assert!(root_table.is_empty()?);
|
|
||||||
|
|
||||||
let persistent_state = models::v1::PersistentState {
|
|
||||||
version: ron_state.version,
|
|
||||||
wall: ron_state.wall,
|
|
||||||
problems: ron_state.problems,
|
|
||||||
};
|
|
||||||
|
|
||||||
root_table.insert((), persistent_state)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tracing::info!("Removing ron state");
|
|
||||||
tokio::fs::remove_file(ron_state_file_path).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Move out, is not really a migration
|
|
||||||
#[tracing::instrument(skip_all, err)]
|
|
||||||
async fn init_at_current_version(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
db.write(|txn| {
|
|
||||||
let mut version_table = txn.open_table(db::TABLE_VERSION)?;
|
|
||||||
let is_missing_version = version_table.get(())?.is_none();
|
|
||||||
if is_missing_version {
|
|
||||||
let v = db::Version::current();
|
|
||||||
tracing::warn!("INITIALIZING DATABASE AT VERSION {v}");
|
|
||||||
version_table.insert((), v)?;
|
|
||||||
|
|
||||||
// Root table
|
|
||||||
{
|
|
||||||
let mut table = txn.open_table(db::current::TABLE_ROOT)?;
|
|
||||||
assert!(table.is_empty()?);
|
|
||||||
table.insert((), models::Root { walls: BTreeSet::new() })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walls table
|
|
||||||
{
|
|
||||||
// Opening the table creates the table
|
|
||||||
let table = txn.open_table(db::current::TABLE_WALLS)?;
|
|
||||||
assert!(table.is_empty()?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Problems table
|
|
||||||
{
|
|
||||||
// Opening the table creates the table
|
|
||||||
let table = txn.open_table(db::current::TABLE_PROBLEMS)?;
|
|
||||||
assert!(table.is_empty()?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, err)]
|
|
||||||
async fn migrate_to_v2(db: &Database) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
use super::db;
|
|
||||||
|
|
||||||
db.write(|txn| {
|
|
||||||
let mut version_table = txn.open_table(db::TABLE_VERSION)?;
|
|
||||||
let version = version_table.get(())?.unwrap().value().version;
|
|
||||||
if version == 1 {
|
|
||||||
tracing::warn!("MIGRATING");
|
|
||||||
version_table.insert((), db::Version { version: 2 })?;
|
|
||||||
|
|
||||||
let root_table_v1 = txn.open_table(db::v1::TABLE_ROOT)?;
|
|
||||||
let root_v1 = root_table_v1.get(())?.unwrap().value();
|
|
||||||
drop(root_table_v1);
|
|
||||||
txn.delete_table(db::v1::TABLE_ROOT)?;
|
|
||||||
|
|
||||||
let models::v1::PersistentState { version: _, wall, problems } = root_v1;
|
|
||||||
|
|
||||||
// we'll reimport them instead of a lossy conversion.
|
|
||||||
drop(problems);
|
|
||||||
|
|
||||||
let mut walls = BTreeSet::new();
|
|
||||||
let wall_uid = models::v2::WallUid(uuid::Uuid::new_v4());
|
|
||||||
let holds = wall
|
|
||||||
.holds
|
|
||||||
.into_iter()
|
|
||||||
.map(|(hold_position, hold)| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let image = hold
|
|
||||||
.image
|
|
||||||
.map(|i| -> Result<_, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let holds_dir = Path::new("datastore/public/holds");
|
|
||||||
|
|
||||||
let p = holds_dir.join(i.filename);
|
|
||||||
tracing::info!("reading {}", p.display());
|
|
||||||
let file_contents = std::fs::read(p)?;
|
|
||||||
|
|
||||||
let mut decoder = image::ImageReader::new(std::io::Cursor::new(file_contents))
|
|
||||||
.with_guessed_format()?
|
|
||||||
.into_decoder()?;
|
|
||||||
let orientation = decoder.orientation()?;
|
|
||||||
let mut img = image::DynamicImage::from_decoder(decoder)?;
|
|
||||||
img.apply_orientation(orientation);
|
|
||||||
|
|
||||||
std::fs::create_dir_all(holds_dir)?;
|
|
||||||
|
|
||||||
let targets = [(50, 50), (150, 150), (300, 300), (400, 400)];
|
|
||||||
|
|
||||||
let uid = models::ImageUid::create();
|
|
||||||
let mut resolutions = BTreeMap::new();
|
|
||||||
for (width, height) in targets {
|
|
||||||
tracing::info!("resizing to {width}x{height}");
|
|
||||||
let resized = img.resize_to_fill(width, height, image::imageops::FilterType::Lanczos3);
|
|
||||||
|
|
||||||
let filename = format!("hold_row{}_col{}_{width}x{height}_{uid}.webp", hold_position.row, hold_position.col);
|
|
||||||
let path = holds_dir.join(&filename);
|
|
||||||
tracing::info!("opening {}", path.display());
|
|
||||||
let mut file = std::fs::OpenOptions::new().write(true).append(false).create_new(true).open(&path)?;
|
|
||||||
resized.write_to(&mut file, image::ImageFormat::WebP)?;
|
|
||||||
|
|
||||||
let res = models::ImageResolution {
|
|
||||||
width: width.into(),
|
|
||||||
height: height.into(),
|
|
||||||
};
|
|
||||||
resolutions.insert(res, models::ImageFilename { filename });
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(models::Image { uid, resolutions })
|
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
models::v1::HoldPosition {
|
|
||||||
row: hold_position.row,
|
|
||||||
col: hold_position.col,
|
|
||||||
},
|
|
||||||
models::v2::Hold {
|
|
||||||
position: models::v1::HoldPosition {
|
|
||||||
row: hold.position.row,
|
|
||||||
col: hold.position.col,
|
|
||||||
},
|
|
||||||
image,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let wall_v2 = models::v2::Wall {
|
|
||||||
uid: wall_uid,
|
|
||||||
rows: wall.rows,
|
|
||||||
cols: wall.cols,
|
|
||||||
holds,
|
|
||||||
problems: BTreeSet::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
walls.insert(wall_v2.uid);
|
|
||||||
let root_v2 = models::v2::Root { walls };
|
|
||||||
|
|
||||||
let mut root_table_v2 = txn.open_table(db::v2::TABLE_ROOT)?;
|
|
||||||
root_table_v2.insert((), root_v2)?;
|
|
||||||
drop(root_table_v2);
|
|
||||||
|
|
||||||
let mut walls_table = txn.open_table(db::v2::TABLE_WALLS)?;
|
|
||||||
walls_table.insert(wall_v2.uid, wall_v2)?;
|
|
||||||
drop(walls_table);
|
|
||||||
|
|
||||||
let problems_table = txn.open_table(db::v2::TABLE_PROBLEMS)?;
|
|
||||||
drop(problems_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
1
justfile
1
justfile
@@ -17,7 +17,6 @@ serve:
|
|||||||
# cargo leptos build --release -vv
|
# cargo leptos build --release -vv
|
||||||
# cp target/release/ascend dist/
|
# cp target/release/ascend dist/
|
||||||
# cp -r target/site dist/
|
# cp -r target/site dist/
|
||||||
|
|
||||||
# run-release:
|
# run-release:
|
||||||
# #!/usr/bin/env bash
|
# #!/usr/bin/env bash
|
||||||
# cd dist
|
# cd dist
|
||||||
|
|||||||
Reference in New Issue
Block a user