From 7f30675aabd6a57f2f914a425617aa51ece83915 Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Sat, 26 Apr 2025 11:17:58 +0300 Subject: [PATCH] Daily update. Wrote some mtgott tests, fixed some bugs --- .gitignore | 3 +- Cargo.lock | 155 ------------------------ Cargo.toml | 3 +- assets/HypertextPages/index.html | 1 - assets/HypertextPages/lang-macro.html | 0 mtgott/src/bin/PRIKOL.rs | 8 -- mtgott/src/dirsearch.rs | 92 +++++++++------ mtgott/src/parser.rs | 12 +- mtgott/tests/april_24.rs | 125 ++++++++++++++++++++ src/lib.rs | 121 ++++++++++++++++++- src/main.rs | 4 +- src/yyyi_ru/mod.rs | 163 -------------------------- 12 files changed, 316 insertions(+), 371 deletions(-) delete mode 100644 assets/HypertextPages/lang-macro.html delete mode 100644 mtgott/src/bin/PRIKOL.rs create mode 100644 mtgott/tests/april_24.rs delete mode 100644 src/yyyi_ru/mod.rs diff --git a/.gitignore b/.gitignore index 35b3418..be68079 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target .idea -src/bin/PRIKOL.rs +src/bin/sandbox.rs +sandbox \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 2f65f0b..9d8cd76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,15 +86,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "bytes" version = "1.10.1" @@ -107,35 +98,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "fnv" version = "1.0.7" @@ -184,16 +146,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "gimli" version = "0.31.1" @@ -287,17 +239,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "libc" version = "0.2.171" @@ -373,51 +314,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pest" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -520,17 +416,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "smallvec" version = "1.15.0" @@ -564,26 +449,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -[[package]] -name = "thiserror" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.44.2" @@ -658,30 +523,12 @@ dependencies = [ "once_cell", ] -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -766,8 +613,6 @@ name = "yyyi_ru" version = "0.1.0" dependencies = [ "axum", - "json5", "mtgott", - "serde_json", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index bec4282..0b98f0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,9 @@ [package] name = "yyyi_ru" version = "0.1.0" +edition = "2024" [dependencies] axum = "0.8.3" tokio = { version = "1.44.1", features = ["rt-multi-thread"] } -json5 = "0.4.1" -serde_json = "1.0.140" mtgott = { path = "./mtgott" } diff --git a/assets/HypertextPages/index.html b/assets/HypertextPages/index.html index dc6ff36..00f3e6d 100644 --- a/assets/HypertextPages/index.html +++ b/assets/HypertextPages/index.html @@ -1,6 +1,5 @@ -{% import "lang_macro.html" as lang_macro %} diff --git a/assets/HypertextPages/lang-macro.html b/assets/HypertextPages/lang-macro.html deleted file mode 100644 index e69de29..0000000 diff --git a/mtgott/src/bin/PRIKOL.rs b/mtgott/src/bin/PRIKOL.rs deleted file mode 100644 index c4b2854..0000000 --- a/mtgott/src/bin/PRIKOL.rs +++ /dev/null @@ -1,8 +0,0 @@ - - -fn main() { - let func : & dyn for<'a> Fn(& 'a u32) -> &'a u32; - func = &|u: &u32| -> &u32 {u}; - let y = 6u32; - println!("{}", func(&y)); -} \ No newline at end of file diff --git a/mtgott/src/dirsearch.rs b/mtgott/src/dirsearch.rs index c2e676b..8470a50 100644 --- a/mtgott/src/dirsearch.rs +++ b/mtgott/src/dirsearch.rs @@ -12,7 +12,7 @@ use std::rc::Rc; pub fn search_dir_rec_helper( res: &mut Vec>, fs_path: PathBuf, virtual_path: String, - allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool, recc: u32 + allowed_extensions: &[&str], is_valid_name: &dyn Fn(&str) -> bool, recc: u32 )-> Result<(), Box> { if recc == 0 { return Err("Recursion limit exceeded".into()); } let fs_path = canonicalize(fs_path)?; @@ -45,65 +45,87 @@ pub fn search_dir_rec_helper( } pub fn search_dir( - p: &str, allowed_extensions: &[&str], is_valid_name: fn(&str) -> bool) + p: &str, allowed_extensions: &[&str], is_valid_name: &dyn Fn(&str) -> bool) -> Result>, Box> { let mut res: Vec> = (0..allowed_extensions.len()).map(|_| Vec::new()).collect(); search_dir_rec_helper(&mut res, PathBuf::from(p), String::new(), allowed_extensions, is_valid_name, 100)?; Ok(res) } -struct MtgottDirContent { - mtgott: Vec, - imtgott: Vec, - plain: Vec +pub struct MtgottDirFiles { + pub mtgott: Vec, + pub imtgott: Vec, + pub plain: Vec } -pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result> { +pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result> { let mut all = search_dir(p, &[ &(String::from(".mtgott") + plain_ext), &(String::from(".imtgott") + plain_ext), plain_ext - ], is_special_name)?; - Ok(MtgottDirContent{ + ], &(|s| !is_special_name(s)))?; + Ok(MtgottDirFiles { mtgott: std::mem::take(&mut all[0]), imtgott: std::mem::take(&mut all[1]), plain: std::mem::take(&mut all[2]), }) } -pub fn get_all_templates(p: &str, plain_ext: &str) -> Result> { - let source = search_mtgott_dir(p, plain_ext)?; +pub struct FileWithPath { + pub v_path: String, + pub text: String, +} + +pub struct MtgottDirContent { + pub mtgott: Vec, + pub imtgott: Vec, + pub plain: Vec, +} + +fn read_file_vector(p: &str, plain_ext: &str, secondary_ext: &str, files: Vec) -> Result, Box> { + files.iter().map(|v_path| -> Result> { + Ok(FileWithPath{ + v_path: v_path.clone(), + text: String::from_utf8(fs::read( + &format!("{p}/{v_path}{secondary_ext}{plain_ext}") + )?)?}) + }).collect::, Box>>() +} + +pub fn read_mtgott_dir_content(p: &str, plain_ext: &str) -> Result> { + let MtgottDirFiles{mtgott, imtgott, plain} = search_mtgott_dir(p, plain_ext)?; + Ok(MtgottDirContent { + mtgott: read_file_vector(p, plain_ext, ".mtgott", mtgott)?, + imtgott: read_file_vector(p, plain_ext, ".imtgott", imtgott)?, + plain: read_file_vector(p, plain_ext, "", plain)?, + }) +} + +pub fn get_all_templates_from_dir_text(dir: MtgottDirContent, plain_ext: &str) -> Result> { let mut res = MTGOTT{root: SharedValue::Dict(HashMap::new()), source_expressions: Vec::new(), source_elements: Vec::new()}; - for cut_path in source.mtgott { - let path = format!("{cut_path}.mtgott{plain_ext}"); - let text_bytes = fs::read(PathBuf::from(p).join(&path))?; - let text = std::str::from_utf8(&text_bytes)?; - let plemege = parse_one_file_packed(text, &cut_path, &mut res.source_expressions)?; + for FileWithPath{v_path, text} in dir.mtgott { + let path = format!("{v_path}.mtgott{}", plain_ext); + let plemege = parse_one_file_packed(&text, &v_path, &mut res.source_expressions)?; let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?; - add_path_to_root(&mut res.root, &cut_path, '/', compiled)? + add_path_with_slashes_to_root(&mut res.root, &v_path, compiled)? } - for cut_path in source.imtgott { - let path = format!("{cut_path}.imtgott{plain_ext}"); - let text_bytes = fs::read(PathBuf::from(p).join(&path))?; - let text = std::str::from_utf8(&text_bytes)?; - let plemege = parse_one_file_simplified(text, &cut_path, &mut res.source_expressions)?; + for FileWithPath{v_path, text} in dir.imtgott { + let path = format!("{v_path}.imtgott{}", plain_ext); + let plemege = parse_one_file_simplified(&text, &v_path, &mut res.source_expressions)?; let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?; - add_path_to_root(&mut res.root, &cut_path, '/', compiled)? + add_path_with_slashes_to_root(&mut res.root, &v_path, compiled)? } - for cut_path in source.plain { - let path = format!("{cut_path}{plain_ext}"); - let text_bytes = fs::read(PathBuf::from(p).join(&path))?; - let text = String::from_utf8(text_bytes)?; - add_path_to_root(&mut res.root, &cut_path, '/', SharedValue::Str(text))? + for FileWithPath{v_path, text} in dir.plain { + add_path_with_slashes_to_root(&mut res.root, &v_path, SharedValue::Str(text))? } Ok(res) } -pub fn get_all_templates_plus_builtins( - p: &str, plain_ext: &str, +pub fn get_all_templates_plus_builtins_from_dir_text( + dir: MtgottDirContent, plain_ext: &str, sanitize: fn(&str) -> String, ) -> Result>{ - let mut res = get_all_templates(p, plain_ext)?; + let mut res = get_all_templates_from_dir_text(dir, plain_ext)?; add_path_with_slashes_to_root(&mut res.root, "sanitize", SharedValue::Fn(Box::new( /* One obvious problem with this sanitizer is that it makes copy even when it * takes full ownership of some string and does not have to replace any characters*/ @@ -124,6 +146,10 @@ pub fn get_all_templates_plus_builtins( Ok(res) } -pub fn get_root_html(p: &str) -> Result>{ - get_all_templates_plus_builtins(p, ".html", escape_for_html) +pub fn get_root_html_from_dir_text_html(dir: MtgottDirContent) -> Result>{ + get_all_templates_plus_builtins_from_dir_text(dir, ".html", escape_for_html) +} + +pub fn get_root_html(p: &str) -> Result> { + get_root_html_from_dir_text_html(read_mtgott_dir_content(p, ".html")?) } diff --git a/mtgott/src/parser.rs b/mtgott/src/parser.rs index ff710fb..8b814b8 100644 --- a/mtgott/src/parser.rs +++ b/mtgott/src/parser.rs @@ -251,13 +251,13 @@ impl<'a> Parser<'a> { return Err(self.new_unexpected_char_error(expected_pack_name)) } let child_name: &str = &self.text[p1..self.p]; - if !is_special_name(child_name) { + if is_special_name(child_name) { return Err(FileParsingError::new(illegal_pack_name, p1, self.p)) } if let Some(_) = res.get(child_name) { return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) } - self.skip_normal_word(); + self.skip_whitespace(); if self.is_ahead("$}"){ self.p += 2; @@ -471,7 +471,7 @@ impl<'a> Parser<'a> { return finishing_touches(ReasonOfElementEnd{p1: self.p, cmd: BlockEndingTag::EOF}, res); } else if self.is_ahead("{{") { fin_static(self, tp1, &mut res); - self.skip_whitespace(); + self.p += 2; if !self.is_ahead("}}") { let expr: Expression = self.parse_expression( arg_names, &mut (0..arg_names.len()).collect(), recc)?; @@ -488,7 +488,7 @@ impl<'a> Parser<'a> { tp1 = self.p; } else if self.is_ahead("{[") { fin_static(self, tp1, &mut res); - self.skip_whitespace(); + self.p += 2; let expr: Expression = self.parse_expression( arg_names, &mut (0..arg_names.len()).collect(), recc)?; res.push(SubElement::InsertExpr(expr)); @@ -750,7 +750,9 @@ impl<'a> Parser<'a> { ) -> Result, FileParsingError> { self.check_recursion_limit(recc)?; self.skip_whitespace(); - if self.is_char_ahead('(') { + if self.is_tag_end_ahead() { + Ok(None) + } else if self.is_char_ahead('(') { self.p += 1; let expr = self.parse_expression(arg_names, used_local_consts, recc - 1)?; self.skip_whitespace(); diff --git a/mtgott/tests/april_24.rs b/mtgott/tests/april_24.rs new file mode 100644 index 0000000..612a806 --- /dev/null +++ b/mtgott/tests/april_24.rs @@ -0,0 +1,125 @@ +extern crate mtgott; +use mtgott::dirsearch::*; +use mtgott::runtime::*; + + +macro_rules! assert_gave { + ($a:expr, $str:literal) => { + assert_eq!($a, String::from($str)) + }; +} + +#[test] +fn t001(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "boba".into(), text: "{$ aboba = \"xdds\" $}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{{ boba.aboba }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "xdds"); +} + + +#[test] +fn t002(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "boba/biba/aaa/bbb".into(), text: "{$ aboba = \"xdds\" $}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{{ boba. biba .aaa.bbb.aboba }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "xdds"); +} + + +#[test] +fn t003(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "boba/biba/aaa/bbb".into(), text: "{$ aboba = \"xdds\" $}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index/yyy/eee".into(), text: "{{ boba. biba .aaa.bbb.aboba }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index.yyy.eee", 50).unwrap(), "xdds"); +} + +#[test] +fn t004(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "boba/biba/aaa/bbb".into(), text: "{$ pack $} {$ AAA = \"xdds\" $} {$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index/yyy/eee".into(), text: "{{ boba. biba .aaa.bbb.pack.AAA }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index.yyy.eee", 50).unwrap(), "xdds"); +} + + +#[test] +fn t005(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "file".into(), text: "{$ pack $} {$ const = 12345 $} {$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{{ file.pack.const }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "12345"); +} + + +#[test] +fn t006(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "file".into(), text: "{$ pack $} {$ const = file.pack.pack2.chislo $} {$ pack2 $} {$ chislo = 123 $} {$} {$}".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{{ file.pack.const }}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "123"); +} + +#[test] +fn t007(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "file".into(), text: "{$ pack $}{$c=45$}{$} {@ el @}---{{this.pack.c}}---{@} ".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{[file.el]}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---45---"); +} + +#[test] +fn t008(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "file".into(), text: "{@ el a@}---{{a}}---{@} ".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{[file.el \"45\"]}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---45---"); +} + +#[test] +fn t009(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "file".into(), text: "{@ el a b c d e f g @}---{{a}}---{{c}}---{{e}}---{{g}}---{@} ".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{[file.el 1 2 3 4 5 6 7]}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "---1---3---5---7---"); +} + + +#[test] +fn t010(){ + let i = MtgottDirContent{mtgott: vec![ + FileWithPath{v_path: "file".into(), text: "{@el a b @} {{a}}{{b}} {@} ".into()} + ], imtgott: vec![ + FileWithPath{v_path: "index".into(), text: "{[file.el 1 2]}".into()} + ], plain: vec![]}; + let r = get_root_html_from_dir_text_html(i).unwrap(); + assert_gave!(r.render(Value::Int(0), "index", 50).unwrap(), "12"); +} diff --git a/src/lib.rs b/src/lib.rs index 5e6a3da..31dd172 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,120 @@ -pub mod yyyi_ru; +use axum; +use std::io; +use std::fs; +use std::collections::HashMap; +use axum::http::HeaderValue; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use axum::http; +use mtgott::dirsearch::{search_dir, get_root_html}; +use mtgott::runtime::{MTGOTT, Value}; +use std::error::Error; + +struct StaticAsset { + content_type: HeaderValue, + content: Vec +} + +struct AssetsCache { + static_assets: HashMap, + pages: MTGOTT, +} + +struct ExtensionContentTypeCorr{ + extension: &'static str, + content_type: HeaderValue, +} + +fn load_static_assets(p: &str, need: &[ExtensionContentTypeCorr]) -> Result, Box> { + let e: Vec<&'static str> = need.iter().map(|corr: &ExtensionContentTypeCorr| corr.extension).collect(); + let content = search_dir(p, &e, &(|_| true))?; + let mut st: HashMap = HashMap::new(); + for i in 0..need.len() { + let extension: &str = need[i].extension; + let content_type: &HeaderValue = &need[i].content_type; + for virtual_path in &content[i] { + let path_org = format!("{p}/{virtual_path}{extension}"); + st.insert(format!("{virtual_path}{extension}"), StaticAsset{ + content_type: content_type.clone(), + content: fs::read(&path_org)?, + }); + } + } + Ok(st) +} + +fn load_needed_static_assets(p: &str) -> Result, Box> { + load_static_assets(p, &[ + ExtensionContentTypeCorr{extension: ".css", content_type: HeaderValue::from_str("text/css").unwrap()}, + ExtensionContentTypeCorr{extension: ".jpeg", content_type: HeaderValue::from_str("image/jpeg").unwrap()}, + ExtensionContentTypeCorr{extension: ".jpg", content_type: HeaderValue::from_str("image/jpeg").unwrap()}, + ExtensionContentTypeCorr{extension: ".png", content_type: HeaderValue::from_str("image/png").unwrap()}, + ]) +} + +impl AssetsCache { + fn load_assets(assets_dir: &str) -> Result> { + Ok(AssetsCache { + static_assets: load_needed_static_assets(assets_dir)?, + pages: get_root_html(&format!("{assets_dir}/HypertextPages"))? + }) + } +} + +async fn static_assets( + axum::extract::Path(path): axum::extract::Path, + axum::extract::State(assets): axum::extract::State>, +) -> Result<([(axum::http::HeaderName, axum::http::HeaderValue); 1], Vec), axum::http::StatusCode> { + if let Some(file) = assets.static_assets.get(&path) { + return Ok(( + [(http::header::CONTENT_TYPE, file.content_type.clone())], + file.content.clone() + )) + } + Err(axum::http::StatusCode::NOT_FOUND) +} + +async fn page_index( + axum::extract::State(assets): axum::extract::State>, + // axum::extract::Extension(t): axum::extract::Extension +) -> axum::response::Html { + let gr = Value::Int(0); + axum::response::Html(assets.pages.render(gr, "index", 500).unwrap()) +} + +async fn page_blog( + axum::extract::State(assets): axum::extract::State>, + // axum::extract::Extension(t): axum::extract::Extension +) -> impl axum::response::IntoResponse { + // let mut context = tera::Context::new(); + // context.insert("lang", &assets.missing_text[0].0); + // context.insert("pres", &assets.missing_text[0].1); + // axum::response::Html(assets.templates.render("blog.html", &context).expect("Tera failure")) + axum::response::Html(String::new()) +} + +async fn fallback_page() -> axum::http::StatusCode { + axum::http::StatusCode::NOT_FOUND +} + +pub async fn run_yyyi_ru() { + let assets_dir = format!("{}/assets", env!("CARGO_MANIFEST_DIR")); + let address = "0.0.0.0:3000"; + println!("Assets dir: {assets_dir:?}"); + + let assets = Arc::new(AssetsCache::load_assets(&assets_dir).unwrap()); + + // build our application with a single route + let app = axum::Router::new() + .without_v07_checks() + .route("/", axum::routing::MethodRouter::new().get(page_index)) + .route("/assets/{*path}", axum::routing::get(static_assets)) + .route("/blog", axum::routing::get(page_blog)) + .fallback(fallback_page).with_state(assets); + // .layer(axum::Extension(templates)); + + // run our app with hyper, listening globally on port 3000 + let listener = tokio::net::TcpListener::bind(address).await.unwrap(); + println!("Running on http://{address}"); + axum::serve(listener, app).await.unwrap(); +} diff --git a/src/main.rs b/src/main.rs index 7b0dcfa..7e6cd79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ -use yyyi_ru::yyyi_ru::MAIN; +use yyyi_ru::run_yyyi_ru; #[tokio::main] async fn main() { - MAIN().await + run_yyyi_ru().await } \ No newline at end of file diff --git a/src/yyyi_ru/mod.rs b/src/yyyi_ru/mod.rs deleted file mode 100644 index 66ecc0c..0000000 --- a/src/yyyi_ru/mod.rs +++ /dev/null @@ -1,163 +0,0 @@ -use axum; -use std::io; -use std::fs; -use std::collections::HashMap; -use axum::http::HeaderValue; -use std::path::{Path, PathBuf}; -use std::sync::Arc; -use axum::http; -use json5; -use serde_json; - -#[derive(Clone)] -struct StaticAsset { - content_type: HeaderValue, - content: Vec -} - -#[derive(Clone)] -struct AssetsCache { - // templates: tera::Tera, - static_assets: HashMap, - missing_text: Vec<(String, serde_json::Value)> -} - -struct ExtensionContentTypeCorr{ - extension: &'static str, - content_type: HeaderValue, -} - -fn read_static_html_page(p: &Path) -> io::Result { - Ok(StaticAsset { - content_type: HeaderValue::from_str("text/html").unwrap(), - content: fs::read(p)? - }) -} - -fn load_static_assets(p: &Path, need: &[ExtensionContentTypeCorr]) -> io::Result> { - if !p.is_dir() { - return Err(io::Error::from(io::ErrorKind::NotADirectory)) - } - let mut st: HashMap = HashMap::new(); - let mut td: Vec = vec![String::new()]; - while td.len() > 0 { - let dshp_dir: String = td.pop().unwrap(); - let p_dir = p.join(dshp_dir.clone()); - for entry in fs::read_dir(p_dir)? { - let entry = entry?; - let dshp_child = if dshp_dir.len() > 0 { - format!("{dshp_dir}/{}", entry.file_name().to_str().unwrap()) - } else { - String::from(entry.file_name().to_str().unwrap()) - }; - let p_child = p.join(dshp_child.clone()); - if p_child.is_dir() { - td.push(dshp_child); - } else { - if let Some(ext) = need.iter().find(|ext| {dshp_child.ends_with(ext.extension)}) { - st.insert(dshp_child.clone(), StaticAsset { - content_type: ext.content_type.clone(), - content: fs::read(p.join(dshp_child))? - }); - } - } - } - } - Ok(st) -} - -fn load_needed_static_assets(p: &Path) -> io::Result> { - load_static_assets(p, &[ - ExtensionContentTypeCorr{extension: ".css", content_type: HeaderValue::from_str("text/css").unwrap()}, - ExtensionContentTypeCorr{extension: ".jpeg", content_type: HeaderValue::from_str("image/jpeg").unwrap()}, - ExtensionContentTypeCorr{extension: ".jpg", content_type: HeaderValue::from_str("image/jpeg").unwrap()}, - ExtensionContentTypeCorr{extension: ".png", content_type: HeaderValue::from_str("image/png").unwrap()}, - ]) -} - -fn load_missing_text(assets: &Path) -> Result, Box> { - let languages = ["ru-RU", "en-US"]; - let mut res: Vec<(String, serde_json::Value)> = Vec::with_capacity(languages.len()); - for language in languages { - let fc = &fs::read(&assets.join(String::from("text/") + language + ".json5"))?; - let val: serde_json::Value = json5::from_str(std::str::from_utf8(fc)?)?; - res.push((String::from(language), val)) - } - Ok(res) -} - -fn load_assets(assets_dir: &Path) -> AssetsCache { - // let tera_st = tera::Tera::new( - // &assets_dir.join("HypertextPages/**/*.html").to_str().unwrap() - // ); - // if let Err(err) = &tera_st { - // println!("{err}") - // } - AssetsCache { - // templates: tera_st.unwrap(), - static_assets: load_needed_static_assets(assets_dir).expect("Failed to load static assets"), - missing_text: load_missing_text(assets_dir).expect("Failed to load gap-text") - } -} - -async fn static_assets( - axum::extract::Path(path): axum::extract::Path, - axum::extract::State(assets): axum::extract::State>, -) -> Result<([(axum::http::HeaderName, axum::http::HeaderValue); 1], Vec), axum::http::StatusCode> { - if let Some(file) = assets.static_assets.get(&path) { - return Ok(( - [(http::header::CONTENT_TYPE, file.content_type.clone())], - file.content.clone() - )) - } - Err(axum::http::StatusCode::NOT_FOUND) -} - -async fn page_index( - axum::extract::State(assets): axum::extract::State>, - // axum::extract::Extension(t): axum::extract::Extension -) -> axum::response::Html { - // let mut context = tera::Context::new(); - // let lang = &assets.missing_text[0].0; - // context.insert("lang", lang); - // context.insert("pres", &assets.missing_text[0].1); - // context.insert("age", &14); - // context.insert("aboutme_template", &(String::from("aboutme/") + lang + ".html")); - // axum::response::Html(assets.templates.render("index.html", &context).expect("Tera failure")) - axum::response::Html(String::new()) -} - -async fn page_blog( - axum::extract::State(assets): axum::extract::State>, - // axum::extract::Extension(t): axum::extract::Extension -) -> impl axum::response::IntoResponse { - // let mut context = tera::Context::new(); - // context.insert("lang", &assets.missing_text[0].0); - // context.insert("pres", &assets.missing_text[0].1); - // axum::response::Html(assets.templates.render("blog.html", &context).expect("Tera failure")) - axum::response::Html(String::new()) -} - -async fn fallback_page() -> axum::http::StatusCode { - axum::http::StatusCode::NOT_FOUND -} - -pub async fn MAIN() { - let assets_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets"); - println!("Cargo manifest dir: {assets_dir:?}"); - - let assets = Arc::new(load_assets(&assets_dir)); - - // build our application with a single route - let app = axum::Router::new() - .without_v07_checks() - .route("/", axum::routing::MethodRouter::new().get(page_index)) - .route("/assets/{*path}", axum::routing::get(static_assets)) - .route("/blog", axum::routing::get(page_blog)) - .fallback(fallback_page).with_state(assets); - // .layer(axum::Extension(templates)); - - // run our app with hyper, listening globally on port 3000 - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); - axum::serve(listener, app).await.unwrap(); -}