diff --git a/Cargo.lock b/Cargo.lock index 0f2a42f..1e975c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,6 +166,15 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -374,6 +383,27 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.13.0" @@ -574,8 +604,10 @@ version = "0.4.0" dependencies = [ "ansiterm", "anyhow", + "bincode", "clap", "console", + "dirs", "gif", "image", "indicatif", @@ -583,6 +615,7 @@ dependencies = [ "rand", "rayon", "reqwest", + "serde", ] [[package]] @@ -910,6 +943,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1175,6 +1218,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "paste" version = "1.0.15" @@ -1411,6 +1460,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "reqwest" version = "0.12.1" diff --git a/Cargo.toml b/Cargo.toml index 917be4e..12206ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,9 @@ rayon = "1.10" reqwest = { version = "0.12", features = ["blocking"] } indicatif = "0.17" console = "0.15" +bincode = "1.3" +serde = { version = "1.0", features = ["derive"] } +dirs = "5.0.1" [profile.release] opt-level = 3 diff --git a/src/eval.rs b/src/eval.rs index 84b6e60..1c6915d 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -46,7 +46,7 @@ struct SumSave { pub struct EvalContext { pub tokens: Vec, pub size: (u32, u32), - pub rgba: [u8; 4], + pub rgba: Rgba, pub saved_rgb: [u8; 3], pub position: (u32, u32), @@ -68,7 +68,12 @@ pub fn eval( } = ctx; let (width, height) = size; let (x, y) = position; - let [r, g, b, a] = rgba; + + let r = rgba[0]; + let g = rgba[1]; + let b = rgba[2]; + let a = rgba[3]; + let [sr, sg, sb] = saved_rgb; if a == 0 { diff --git a/src/main.rs b/src/main.rs index 1d6f192..e63eb8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,12 +20,15 @@ use indicatif::{ProgressBar, ProgressStyle}; use rand::prelude::StdRng; use rand::{RngCore, SeedableRng}; use rayon::prelude::*; -use std::fs; -use std::io::{BufRead, BufReader, BufWriter, Read}; +use std::{env, fs}; +use std::fs::File; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::io::{BufRead, BufReader, BufWriter, Read, Write}; use std::iter::Filter; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::time::Duration; +use dirs::home_dir; mod bounds; mod eval; @@ -87,9 +90,7 @@ fn main() -> anyhow::Result<()> { .expect("Failed to set thread count"); } - // If we want to pass the arguments to a function, we need to clone them if args.input.starts_with("http") { - // use the writer println!( "{} Downloading Image: {}", DOWNLOAD, @@ -136,47 +137,64 @@ fn main() -> anyhow::Result<()> { args.expressions.extend(expressions); } - println!( - "{} Parsing {} Expression{}...", - LOOKING_GLASS, - style(&args.expressions.len()).bold().cyan(), - if args.expressions.len() > 1 { "s" } else { "" } - ); + let expression_list_hash = hash_strings(args.expressions.clone()); + let load_parsed_from_cache = get_precompiled_cache(format!("{}", expression_list_hash).as_str()); let mut parsed: Vec<(String, Vec)> = vec![]; - let mut idx = 1; - let expression_count = args.expressions.len(); - for e in &args.expressions { - let spinner = ProgressBar::new_spinner(); - spinner.set_style( - ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {wide_msg}")? - .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ") + + if let Some(cache) = load_parsed_from_cache { + let serialized = fs::read(cache)?; + parsed = bincode::deserialize(&serialized)?; + println!( + "{} Loaded {} Expression{} from cache...", + OK, + style(parsed.len()).bold().cyan(), + if parsed.len() > 1 { "s" } else { "" } ); - spinner.set_message(format!("Parsing [{}/{}] {}", idx, expression_count, style(e).bold().cyan())); - spinner.enable_steady_tick(Duration::from_millis(100)); + } else { + println!( + "{} Parsing {} Expression{}...", + LOOKING_GLASS, + style(&args.expressions.len()).bold().cyan(), + if args.expressions.len() > 1 { "s" } else { "" } + ); + let mut idx = 1; + let expression_count = args.expressions.len(); + for e in &args.expressions { + let spinner = ProgressBar::new_spinner(); + spinner.set_style( + ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {wide_msg}")? + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ") + ); + spinner.set_message(format!("Parsing [{}/{}] {}", idx, expression_count, style(e).bold().cyan())); + spinner.enable_steady_tick(Duration::from_millis(100)); - let tokens = match parser::shunting_yard(e) { - Ok(tokens) => tokens, - Err(err) => { - spinner.finish_and_clear(); + let tokens = match parser::shunting_yard(e) { + Ok(tokens) => tokens, + Err(err) => { + spinner.finish_and_clear(); - println!("{} Expression {} failed to parse...", ERROR, style(e).bold().cyan()); - println!("{} {} -> {}", ERROR, style("ERROR").red().bold(), err); - return Ok(()); + println!("{} Expression {} failed to parse...", ERROR, style(e).bold().cyan()); + println!("{} {} -> {}", ERROR, style("ERROR").red().bold(), err); + return Ok(()); + } + }; + spinner.finish_and_clear(); + + println!("{} [{}/{}] Parsed {} tokens from -> {}", OK, idx, expression_count, style(tokens.len()).cyan().bold(), style(e).bold().cyan()); + + if args.verbose { + tokens.clone().iter().for_each(|t| { + println!("\t{}", t); + }); } - }; - spinner.finish_and_clear(); - println!("{} [{}/{}] Parsed {} tokens from -> {}", OK, idx, expression_count, style(tokens.len()).cyan().bold(), style(e).bold().cyan()); + idx += 1; - if args.verbose { - tokens.clone().iter().for_each(|t| { - println!("\t{}", t); - }); + parsed.push((e.to_string(), tokens)); } - idx += 1; - - parsed.push((e.to_string(), tokens)); + let serialized = bincode::serialize(&parsed)?; + save_to_cache(format!("{}", expression_list_hash).as_str(), &serialized)?; } handle_image(&args, &parsed, &mut rng)?; @@ -391,7 +409,7 @@ fn process( EvalContext { tokens: tokens.clone(), size: (width, height), - rgba: colors.0, + rgba: colors, saved_rgb: [sr, sg, sb], position: (x, y), ignore_state: args.no_state, @@ -436,4 +454,56 @@ fn get_random_seed(args: &Args) -> u64 { } args.seed.unwrap() +} + +fn hash_strings(strings: Vec) -> u64 { + let mut hasher = DefaultHasher::new(); + for s in strings { + s.hash(&mut hasher); + } + hasher.finish() +} + +fn get_precompiled_cache(hash: &str) -> Option { + let home_dir = home_dir().expect("Failed to find home directory"); + + let glitch_dir = Path::new(&home_dir).join(".glitch"); + if !glitch_dir.exists() { + fs::create_dir(&glitch_dir).expect("Failed to create .glitch directory"); + } + + let cache_dir = glitch_dir.join("cache"); + if !cache_dir.exists() { + fs::create_dir(&cache_dir).expect("Failed to create cache directory"); + } + + let cached_file_path = cache_dir.join(hash); + + if cached_file_path.exists() { + Some(cached_file_path) + } else { + None + } +} + +fn save_to_cache(hash: &str, data: &[u8]) -> anyhow::Result { + let home_dir = home_dir().expect("Failed to find home directory"); + + let glitch_dir = Path::new(&home_dir).join(".glitch"); + if !glitch_dir.exists() { + fs::create_dir(&glitch_dir)?; + } + + let cache_dir = glitch_dir.join("cache"); + if !cache_dir.exists() { + fs::create_dir(&cache_dir)?; + } + + let cached_file_path = cache_dir.join(hash); + + // Write data to the file + let mut file = File::create(&cached_file_path)?; + file.write_all(data)?; + + Ok(cached_file_path) } \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs index 3eac33d..37b562e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,8 +2,9 @@ use ansiterm::{Color, Style}; use std::collections::VecDeque; use std::fmt::Formatter; +use serde::{Serialize, Deserialize}; -#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Serialize, Deserialize)] pub enum Token { Num(u8), Random(u8),