Just works(TM). Even wasm works and loading goes BRRRRRR

This commit is contained in:
David Holland 2023-09-15 13:33:40 +02:00
parent 54200678ab
commit d03951dce1
Signed by: DustVoice
GPG Key ID: 47068995A14EDCA9
9 changed files with 133 additions and 246 deletions

174
Cargo.lock generated
View File

@ -93,17 +93,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aes"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.8.3"
@ -421,12 +410,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -526,27 +509,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "bzip2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cairo-sys-rs"
version = "0.15.1"
@ -647,16 +609,6 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "clipboard-win"
version = "4.5.0"
@ -743,12 +695,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -826,12 +772,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "deranged"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]]
name = "derivative"
version = "2.2.0"
@ -851,7 +791,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -1442,15 +1381,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "home"
version = "0.5.5"
@ -1523,15 +1453,6 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -2068,35 +1989,12 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "password-hash"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
"hmac",
"password-hash",
"sha2",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
@ -2485,17 +2383,6 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "signal-hook"
version = "0.3.17"
@ -2608,12 +2495,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"
@ -2697,23 +2578,6 @@ dependencies = [
"syn 2.0.33",
]
[[package]]
name = "time"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48"
dependencies = [
"deranged",
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
[[package]]
name = "tiny-skia"
version = "0.8.4"
@ -3541,48 +3405,10 @@ version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"aes",
"byteorder",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"flate2",
"hmac",
"pbkdf2",
"sha1",
"time",
"zstd",
]
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "5.0.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
dependencies = [
"libc",
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.8+zstd.1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]

View File

@ -20,7 +20,7 @@ egui = "0.22.0"
eframe = "0.22.0"
log = "0.4.20"
async-std = "1.12.0"
zip = "0.6.6"
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]

View File

@ -24,8 +24,6 @@
<link data-trunk rel="copy-file" href="assets/icon_ios_touch_192.png" />
<link data-trunk rel="copy-file" href="assets/maskable_icon_x512.png" />
<link data-trunk rel="copy-dir" href="beanconqueror" data-target-path="beanconqueror" />
<link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="icon_ios_touch_192.png">

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly"
channel = "stable"
components = [ "rustfmt", "rust-analyzer" ]

View File

@ -35,6 +35,9 @@ pub struct Config {
pub width: u32,
pub height: u32,
#[serde(skip)]
pub data: Option<HashMap<String, String>>,
}
pub const WGHT_SHEET: usize = 0;
@ -52,15 +55,37 @@ pub struct Data {
impl Config {
pub fn from_file(path: &str) -> Self {
let config_file = fs::read_to_string(&path).expect("Can't read config.toml");
toml::from_str(&config_file).expect("Can't deserialize config.toml")
Self::from_str(&config_file)
}
pub fn from_raw(bytes: &Vec<u8>) -> Self {
let config_file = core::str::from_utf8(&bytes).expect("Not valid UTF-8");
toml::from_str(&config_file).expect("Can't deserialize config.toml")
pub fn from_str(file: &str) -> Self {
toml::from_str(&file).expect("Can't deserialize config.toml")
}
pub fn from_default() -> Self {
Self::from_file("config.toml")
}
pub fn with_data(mut self, data: HashMap<String, String>) -> Self {
self.data = Some(data);
self
}
pub fn get_file(&self, file: &str) -> Option<&String> {
if let Some(data) = &self.data {
data.get(file)
} else {
None
}
}
pub fn get_filename(&self, filename: &str) -> String {
format!("{}/{}", &self.data_dir, &filename)
}
pub fn get_main_file(&self) -> &String {
let main_filename = &self.get_filename(&self.main_json);
self.get_file(&main_filename).expect("Can't get main file {main_filename}")
}
}

View File

@ -62,23 +62,26 @@ pub struct Config {
impl Database {
pub fn from_file(path: &str) -> Self {
let file = fs::File::open(path)
let file = fs::read_to_string(path)
.unwrap_or_else(|_| panic!("Cannot open file at path \"{}\"", path));
let database: Self = serde_json::from_reader(file)
.unwrap_or_else(|_| panic!("Cannot deserialize file at path \"{}\"", path));
database
Self::from_str(&file)
}
pub fn from_string(string: &String) -> Self {
let database: Self = serde_json::from_str(&string)
pub fn from_str(str: &str) -> Self {
let database: Self = serde_json::from_str(&str)
.unwrap_or_else(|_| panic!("Cannot deserialize file from data"));
database
}
pub fn from_config(config: &config::Config) -> Self {
Database::from_file(&format!("{}/{}", &config.data_dir, &config.main_json))
if config.data.is_some() {
Database::from_str(
&config.get_main_file()
)
} else {
Database::from_file(&config.get_filename(&config.main_json))
}
}
pub fn bean_names(&self) -> Vec<String> {
@ -149,7 +152,7 @@ impl Brew {
}
pub fn date_time(&self, database: &Database) -> String {
if let Some(bean) = &database.bean_for_uuid(&self.bean) {
if database.bean_for_uuid(&self.bean).is_some() {
unix_to_human_date_time(self.config.unix_timestamp.to_owned())
} else {
String::default()

View File

@ -57,10 +57,14 @@ pub struct DataCollection {
impl FlowProfile {
pub fn from_file(path: &str, cutoff: Option<f64>) -> Self {
let file = fs::File::open(path)
let file = fs::read_to_string(path)
.unwrap_or_else(|_| panic!("Cannot open file at path \"{}\"", path));
let mut brew: Self = serde_json::from_reader(file)
.unwrap_or_else(|_| panic!("Cannot deserialize file at path \"{}\"", path));
FlowProfile::from_str(&file, cutoff)
}
pub fn from_str(str: &str, cutoff: Option<f64>) -> Self {
let mut brew: Self = serde_json::from_str(&str)
.unwrap_or_else(|_| panic!("Cannot deserialize file"));
brew.cutoff = cutoff;
brew

View File

@ -1,13 +1,10 @@
use crate::config::Config;
use crate::database::{Brew, Database};
use crate::flow_profile::FlowProfile;
use crate::sheets::load_data;
use crate::time::{unix_to_human_date, unix_to_human_date_time, unix_to_machine_date};
use egui::plot::{PlotPoint, PlotPoints};
use std::collections::HashMap;
use std::fs;
use std::sync::{Arc, Mutex};
pub type RawPlotPoints = Vec<PlotPoint>;
@ -28,14 +25,17 @@ pub fn plot_points_to_owned(plot_points: &RawPlotPoints) -> PlotPoints {
pub fn database_plot_entry(brew: &Brew, config: &Config, database: &Database) -> Option<PlotEntry> {
let mut plot_points: Option<(String, RawPlotPoints)> = None;
if let Some(flow_profile) = &brew.flow_profile {
if !&flow_profile.is_empty() {
if let Some(brew_flow_profile) = &brew.flow_profile {
if !&brew_flow_profile.is_empty() {
let brew_title = brew.date_time_with_bean(&database);
// TODO: CHANGE to raw. Maybe include some Option<ZipArchive...> in Config?!
let flow_profile =
FlowProfile::from_file(&format!("{}/{}", &config.data_dir, &flow_profile), None)
.preprocess_json();
let flow_profile: FlowProfile;
let flow_profile_filename = &config.get_filename(&brew_flow_profile);
if let Some(file) = &config.get_file(&flow_profile_filename) {
flow_profile = FlowProfile::from_str(&file, None).preprocess_json();
} else {
flow_profile = FlowProfile::from_file(&flow_profile_filename, None).preprocess_json();
}
if let Some(data_collection) = &flow_profile.data_collection {
let point_vec: Vec<[f64; 2]> = data_collection

117
src/ui.rs
View File

@ -3,8 +3,7 @@
use eframe::egui;
use egui::{plot::{Legend, Line, Plot}, Align, Layout, ProgressBar, Modifiers};
use rfd::{FileHandle, AsyncFileDialog};
use zip::ZipArchive;
use rfd::{AsyncFileDialog, AsyncMessageDialog};
use crate::{
config::Config,
@ -14,7 +13,7 @@ use crate::{
use std::{
collections::HashMap,
thread::{self, JoinHandle}, io::{Read, Cursor}, default,
io::{Read, Cursor}
};
use std::{
env,
@ -40,9 +39,10 @@ pub struct Ui {
loading_progress: Arc<Mutex<LoadingProgress>>,
loading_data: Arc<Mutex<Option<LoadingData>>>,
config_file: Arc<Mutex<Option<Vec<u8>>>>,
zip_file: Arc<Mutex<Option<ZipArchive<Cursor<Vec<u8>>>>>>,
config_file: Arc<Mutex<Option<String>>>,
data_archive: Arc<Mutex<Option<HashMap<String, String>>>>,
data_transfered: bool,
files_provided: Arc<Mutex<bool>>,
config: Config,
database: Database,
@ -77,13 +77,8 @@ impl Ui {
.brews_for_uuids(&self.get_selected_brew_uuids())
}
pub fn reload(&mut self, _ctx: &egui::Context) {
if self.data_transfered {
self.data_transfered = false;
let mut loading_progress_lock = self.loading_progress.lock().unwrap();
*loading_progress_lock = LoadingProgress::default();
}
pub fn reset(&mut self) {
*self = Self::default();
}
}
@ -101,10 +96,10 @@ impl eframe::App for Ui {
egui::TopBottomPanel::top("dark_light").show(ctx, |ui| {
ui.vertical(|ui| {
egui::menu::bar(ui, |ui| {
let reload_button = ui.button("Reload");
let reset_button = ui.button("Reset");
if reload_button.clicked() {
self.reload(ctx);
if reset_button.clicked() {
self.reset();
}
// if reload_button.hovered() {
@ -316,11 +311,7 @@ impl eframe::App for Ui {
ui.with_layout(
Layout::top_down(Align::Center).with_main_align(Align::Center),
|ui| {
let file_loaded: bool;
{
file_loaded = self.config_file.lock().unwrap().is_some();
}
if file_loaded {
if *self.files_provided.lock().unwrap() {
let loading_finished: bool;
{
loading_finished = self.loading_progress.lock().unwrap().finished;
@ -331,7 +322,7 @@ impl eframe::App for Ui {
self.show_loading_screen = true;
let config_file_arc = self.config_file.clone();
let zip_file_arc = self.zip_file.clone();
let data_archive_arc = self.data_archive.clone();
let loading_data_arc = self.loading_data.clone();
let loading_progress_arc = self.loading_progress.clone();
let ctx_clone = ctx.clone();
@ -341,17 +332,12 @@ impl eframe::App for Ui {
let mut loading_data = LoadingData::default();
let config_file_lock = config_file_arc.lock().unwrap();
let mut zip_file_lock = zip_file_arc.lock().unwrap();
let mut config_file_lock = config_file_arc.lock().unwrap();
let mut data_archive_lock = data_archive_arc.lock().unwrap();
loading_data.config = Config::from_raw(config_file_lock.as_ref().expect("No config file loaded"));
loading_data.config = Config::from_str(&config_file_lock.take().unwrap()).with_data(data_archive_lock.take().unwrap());
let main_filename = format!("{}/{}", &loading_data.config.data_dir, &loading_data.config.main_json);
let mut zip_file = zip_file_lock.as_mut().expect("No data zip file loaded");
let mut main_file = zip_file.by_name(&main_filename).expect("Can't get main file {main_filename}");
let mut main_file_string = String::new();
main_file.read_to_string(&mut main_file_string).expect("Can't read main file {main_file} to string");
loading_data.database = Database::from_string(&main_file_string);
loading_data.database = Database::from_config(&loading_data.config);
let mut checkboxes = HashMap::new();
@ -413,6 +399,14 @@ impl eframe::App for Ui {
} else {
Plot::new("Combined Chart of selected brews")
.legend(Legend::default())
.label_formatter(|name, value| {
let coord = format!("{:.1}g @ {:.1}s", value.y, value.x);
if !name.is_empty() {
format!("{} [ {} ]", coord, name)
} else {
coord
}
})
.show(ui, |plot_ui| {
for brew_checkbox in &self.brew_checkboxes {
if *brew_checkbox.1 {
@ -432,13 +426,21 @@ impl eframe::App for Ui {
}
}
} else {
ui.label("Select the config file (.toml) to start");
ui.label("Select the required files to start");
if ui.button("Load file").clicked() {
if ui.button("Load files").clicked() {
let config_file_arc = self.config_file.clone();
let zip_file_arc = self.zip_file.clone();
let data_archive_arc = self.data_archive.clone();
let files_provided_arc = self.files_provided.clone();
let file_dialog = async move {
let _config_message = AsyncMessageDialog::new()
.set_title("Select config file")
.set_description("Please select the config (.toml) file in the next dialog.\n(Default \"config.toml\")")
.set_buttons(rfd::MessageButtons::Ok)
.set_level(rfd::MessageLevel::Info)
.show().await;
let config_file = AsyncFileDialog::new()
.add_filter("toml", &["toml"])
.set_directory(
@ -451,9 +453,16 @@ impl eframe::App for Ui {
.set_file_name("config.toml")
.pick_file().await;
let config_data = config_file.unwrap().read().await;
let config_raw = config_file.unwrap().read().await;
let zip_file = AsyncFileDialog::new()
let _archive_message = AsyncMessageDialog::new()
.set_title("Select data archive")
.set_description("Please select the data archive (.zip) in the next dialog.\n(Default \"config.toml\")\n\nWihin the zip archive there should be the main json file (e.g. \"Beanconqueror.json\"), as well as a \"brews\" folder, with exact paths specified in the previously selected config file.")
.set_buttons(rfd::MessageButtons::Ok)
.set_level(rfd::MessageLevel::Info)
.show().await;
let archive_file = AsyncFileDialog::new()
.add_filter("zip", &["zip"])
.set_directory(
match &env::current_dir() {
@ -465,20 +474,42 @@ impl eframe::App for Ui {
.set_file_name("beanconqueror.zip")
.pick_file().await;
let zip_data = zip_file.unwrap().read().await;
let archive_raw = archive_file.unwrap().read().await;
let config_content = String::from_utf8(config_raw)
.expect("Can't convert provided config file to UTF-8");
let mut config_file_lock = config_file_arc.lock().unwrap();
*config_file_lock = Some(config_data);
*config_file_lock = Some(config_content);
if let Ok(zip) = zip::ZipArchive::new(Cursor::new(zip_data)) {
println!("Files inside zip:");
for name in zip.file_names() {
println!("Name: {name}");
let mut data_archive: Option<HashMap<String, String>> = None;
if let Ok(mut zip) = zip::ZipArchive::new(Cursor::new(archive_raw)) {
let mut unzip: HashMap<String, String> = HashMap::new();
for i in 0..zip.len() {
if let Ok(mut file) = zip.by_index(i) {
if file.is_file() {
let mut content: String = String::new();
if file.read_to_string(&mut content).is_ok() {
if let Some(enclosed_name) = &file.enclosed_name() {
if let Some(filename) = enclosed_name.to_str() {
unzip.insert(filename.to_string(), content);
}
}
}
}
}
}
let mut zip_file_lock = zip_file_arc.lock().unwrap();
*zip_file_lock = Some(zip);
data_archive = Some(unzip);
}
let mut data_archive_lock = data_archive_arc.lock().unwrap();
*data_archive_lock = data_archive;
let mut files_provided_lock = files_provided_arc.lock().unwrap();
*files_provided_lock = true;
};
#[cfg(not(target_arch = "wasm32"))]