From cef98173e38d9f422fbbcfa1a48188cebb26ef38 Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 3 Sep 2022 08:59:06 -0400 Subject: [PATCH] dingus ocringus --- client/OrangeGuidanceTomestone.csproj | 3 +- client/Pack.cs | 22 +++++ client/PluginUi.cs | 128 +++++++++++++++++++++++--- server/.env | 1 + server/.gitignore | 1 + server/Cargo.lock | 10 ++ server/Cargo.toml | 1 + server/src/config.rs | 11 +++ server/src/main.rs | 24 ++++- server/src/web/vote.rs | 3 +- 10 files changed, 183 insertions(+), 21 deletions(-) create mode 100644 server/.env create mode 100644 server/src/config.rs diff --git a/client/OrangeGuidanceTomestone.csproj b/client/OrangeGuidanceTomestone.csproj index 6fb78bf..b345367 100755 --- a/client/OrangeGuidanceTomestone.csproj +++ b/client/OrangeGuidanceTomestone.csproj @@ -64,8 +64,7 @@ - - + diff --git a/client/Pack.cs b/client/Pack.cs index dfe6cca..11df96f 100644 --- a/client/Pack.cs +++ b/client/Pack.cs @@ -1,7 +1,29 @@ +using YamlDotNet.Serialization.NamingConventions; + namespace OrangeGuidanceTomestone; [Serializable] public class Pack { + internal static Lazy All { get; } = new(() => { + var des = new YamlDotNet.Serialization.DeserializerBuilder() + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .Build(); + return new[] { + "ffxiv", + "elden-ring", + "dark-souls", + } + .Select(name => { + try { + return des.Deserialize(Resourcer.Resource.AsString($"{name}.yaml")); + } catch { + return null; + } + }) + .Where(pack => pack != null) + .ToArray()!; + }); + public string Name { get; init; } public Guid Id { get; init; } public string[] Templates { get; init; } diff --git a/client/PluginUi.cs b/client/PluginUi.cs index e579a1f..7899aea 100644 --- a/client/PluginUi.cs +++ b/client/PluginUi.cs @@ -1,6 +1,5 @@ using System.Text; using ImGuiNET; -using YamlDotNet.Serialization.NamingConventions; namespace OrangeGuidanceTomestone; @@ -33,14 +32,40 @@ public class PluginUi : IDisposable { return; } + var packPrev = Pack.All.Value[this._pack].Name; + if (ImGui.BeginCombo("Pack", packPrev)) { + for (var i = 0; i < Pack.All.Value.Length; i++) { + var selPack = Pack.All.Value[i]; + if (!ImGui.Selectable(selPack.Name)) { + continue; + } + + this._pack = i; + + this._part1 = -1; + this._word1 = (-1, -1); + this._conj = -1; + this._part2 = -1; + this._word2 = (-1, -1); + } + + ImGui.EndCombo(); + } + + const string placeholder = "****"; + void DrawPicker(string id, IReadOnlyList items, ref int x) { - var preview = x == -1 ? "" : items[x].Replace("{0}", "****"); + var preview = x == -1 ? "" : items[x].Replace("{0}", placeholder); if (!ImGui.BeginCombo(id, preview)) { return; } + if (ImGui.Selectable("")) { + x = -1; + } + for (var i = 0; i < items.Count; i++) { - var template = items[i].Replace("{0}", "****"); + var template = items[i].Replace("{0}", placeholder); if (ImGui.Selectable(template, i == x)) { x = i; } @@ -73,18 +98,15 @@ public class PluginUi : IDisposable { ImGui.EndCombo(); } - var pack = new YamlDotNet.Serialization.DeserializerBuilder() - .WithNamingConvention(UnderscoredNamingConvention.Instance) - .Build() - .Deserialize(Resourcer.Resource.AsString("elden-ring.yaml")); + var pack = Pack.All.Value[this._pack]; if (this._part1 == -1) { - ImGui.TextUnformatted("****"); + ImGui.TextUnformatted(placeholder); } else { var preview = new StringBuilder(); var template1 = pack.Templates[this._part1]; - var word1 = this._word1 == (-1, -1) ? "****" : pack.Words[this._word1.Item1].Words[this._word1.Item2]; + var word1 = this._word1 == (-1, -1) ? placeholder : pack.Words[this._word1.Item1].Words[this._word1.Item2]; preview.Append(string.Format(template1, word1)); if (this._conj != -1) { @@ -98,7 +120,7 @@ public class PluginUi : IDisposable { if (this._part2 != -1) { var template2 = pack.Templates[this._part2]; - var word2 = this._word2 == (-1, -1) ? "****" : pack.Words[this._word2.Item1].Words[this._word2.Item2]; + var word2 = this._word2 == (-1, -1) ? placeholder : pack.Words[this._word2.Item1].Words[this._word2.Item2]; preview.Append(string.Format(template2, word2)); } } @@ -109,12 +131,90 @@ public class PluginUi : IDisposable { ImGui.Separator(); DrawPicker("Template##part-1", pack.Templates, ref this._part1); - DrawWordPicker("Word##word-1", pack.Words, ref this._word1); - DrawPicker("Conjugation##conj", pack.Conjunctions, ref this._conj); - DrawPicker("Template##part-2", pack.Templates, ref this._part2); - DrawWordPicker("Word##word-2", pack.Words, ref this._word2); + if (this._part1 > -1 && pack.Templates[this._part1].Contains("{0}")) { + DrawWordPicker("Word##word-1", pack.Words, ref this._word1); + } + DrawPicker("Conjunction##conj", pack.Conjunctions, ref this._conj); + + if (this._conj != -1) { + DrawPicker("Template##part-2", pack.Templates, ref this._part2); + if (this._part2 > -1 && pack.Templates[this._part2].Contains("{0}")) { + DrawWordPicker("Word##word-2", pack.Words, ref this._word2); + } + } + + this.ClearIfNecessary(); + + var valid = this.ValidSetup(); + if (valid) { + ImGui.BeginDisabled(); + } + + if (ImGui.Button("Write") && valid) { + } + + if (valid) { + ImGui.EndDisabled(); + } ImGui.End(); } + + private void ClearIfNecessary() { + if (this._pack == -1) { + this._part1 = -1; + } + + var pack = Pack.All.Value[this._pack]; + + if (this._part1 == -1 || !pack.Templates[this._part1].Contains("{0}")) { + this._word1 = (-1, -1); + } + + if (this._conj == -1) { + this._part2 = -1; + } + + if (this._part2 == -1 || !pack.Templates[this._part2].Contains("{0}")) { + this._word2 = (-1, -1); + } + } + + private bool ValidSetup() { + if (this._pack == -1 || this._part1 == -1) { + return false; + } + + var pack = Pack.All.Value[this._pack]; + var template1 = pack.Templates[this._part1]; + var temp1Variable = template1.Contains("{0}"); + + switch (temp1Variable) { + case true when this._word1 == (-1, -1): + case false when this._word1 != (-1, -1): + return false; + } + + if (this._conj == -1 && (this._part2 != -1 || this._word2 != (-1, -1))) { + return false; + } + + if (this._conj != -1) { + if (this._part2 == -1) { + return false; + } + + var template2 = pack.Templates[this._part2]; + var temp2Variable = template2.Contains("{0}"); + + switch (temp2Variable) { + case true when this._word2 == (-1, -1): + case false when this._word2 != (-1, -1): + return false; + } + } + + return true; + } } diff --git a/server/.env b/server/.env new file mode 100644 index 0000000..b2a38e5 --- /dev/null +++ b/server/.env @@ -0,0 +1 @@ +DATABASE_URL="sqlite://database.sqlite" diff --git a/server/.gitignore b/server/.gitignore index aa193d1..33363fe 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,2 +1,3 @@ /target /database.sqlite* +/config.toml diff --git a/server/Cargo.lock b/server/Cargo.lock index 88e5f5b..83da124 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1038,6 +1038,7 @@ dependencies = [ "sha3", "sqlx", "tokio", + "toml", "uuid", "warp", ] @@ -1388,6 +1389,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "tower-service" version = "0.3.2" diff --git a/server/Cargo.toml b/server/Cargo.toml index ef3e977..d232ab1 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,6 +13,7 @@ serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" sha3 = "0.10" sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite", "chrono"] } +toml = "0.5" tokio = { version = "1", features = ["rt-multi-thread", "macros"] } uuid = { version = "1", features = ["serde", "v4"] } warp = "0.3" diff --git a/server/src/config.rs b/server/src/config.rs new file mode 100644 index 0000000..880761e --- /dev/null +++ b/server/src/config.rs @@ -0,0 +1,11 @@ +use std::net::SocketAddr; +use std::path::PathBuf; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Config { + pub address: SocketAddr, + pub packs: PathBuf, + pub database: String, +} diff --git a/server/src/main.rs b/server/src/main.rs index 70cea9a..cff3af1 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,7 +1,6 @@ #![feature(let_chains)] use std::collections::HashMap; -use std::net::SocketAddr; use std::sync::Arc; use anyhow::{Context, Result}; @@ -11,16 +10,19 @@ use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use tokio::sync::RwLock; use uuid::Uuid; +use crate::config::Config; use crate::pack::Pack; mod pack; mod message; mod web; mod util; +mod config; static MIGRATOR: Migrator = sqlx::migrate!(); pub struct State { + pub config: Config, pub db: Pool, pub packs: RwLock>, } @@ -29,7 +31,7 @@ impl State { pub async fn update_packs(&self) -> Result<()> { let mut packs = HashMap::new(); - let mut dir = tokio::fs::read_dir("packs").await?; + let mut dir = tokio::fs::read_dir(&self.config.packs).await?; while let Ok(Some(entry)) = dir.next_entry().await { if !entry.path().is_file() { continue; @@ -64,6 +66,18 @@ impl State { #[tokio::main] async fn main() -> Result<()> { + let args: Vec = std::env::args().skip(1).collect(); + if args.is_empty() { + eprintln!("usage: server [config]"); + return Ok(()); + } + + let config_str = tokio::fs::read_to_string(&args[0]) + .await + .with_context(|| format!("could not read config file at {}", args[0]))?; + let config: Config = toml::from_str(&config_str) + .context("could not parse config file")?; + let options = SqliteConnectOptions::new(); // options.log_statements(LevelFilter::Debug); @@ -76,7 +90,7 @@ async fn main() -> Result<()> { Ok(()) })) // .connect_with(options.filename(&config.database.path)) - .connect_with(options.filename("./database.sqlite")) + .connect_with(options.filename(&config.database)) .await .context("could not connect to database")?; MIGRATOR.run(&pool) @@ -84,12 +98,14 @@ async fn main() -> Result<()> { .context("could not run database migrations")?; let state = Arc::new(State { + config, db: pool, packs: Default::default(), }); state.update_packs().await?; - warp::serve(web::routes(state)).run("127.0.0.1:8080".parse::()?).await; + let address = state.config.address.clone(); + warp::serve(web::routes(state)).run(address).await; Ok(()) } diff --git a/server/src/web/vote.rs b/server/src/web/vote.rs index b7cd5be..5e99435 100644 --- a/server/src/web/vote.rs +++ b/server/src/web/vote.rs @@ -30,10 +30,11 @@ async fn logic(state: Arc, id: i64, message_id: Uuid, vote: i8) -> Result }; sqlx::query!( // language=sqlite - "insert or ignore into votes (user, message, vote) values (?, ?, ?)", + "insert into votes (user, message, vote) values (?, ?, ?) on conflict do update set vote = ?", id, message_id, vote, + vote, ) .execute(&state.db) .await