Daily update. Wrote some mtgott tests, fixed some bugs

This commit is contained in:
Андреев Григорий 2025-04-26 11:17:58 +03:00
parent 9ce0e602b3
commit 7f30675aab
12 changed files with 316 additions and 371 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
target target
.idea .idea
src/bin/PRIKOL.rs src/bin/sandbox.rs
sandbox

155
Cargo.lock generated
View File

@ -86,15 +86,6 @@ dependencies = [
"windows-targets", "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]] [[package]]
name = "bytes" name = "bytes"
version = "1.10.1" version = "1.10.1"
@ -107,35 +98,6 @@ 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 = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -184,16 +146,6 @@ dependencies = [
"pin-utils", "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]] [[package]]
name = "gimli" name = "gimli"
version = "0.31.1" version = "0.31.1"
@ -287,17 +239,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 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]] [[package]]
name = "libc" name = "libc"
version = "0.2.171" version = "0.2.171"
@ -373,51 +314,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 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]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.16"
@ -520,17 +416,6 @@ dependencies = [
"serde", "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]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.0" version = "1.15.0"
@ -564,26 +449,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 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]] [[package]]
name = "tokio" name = "tokio"
version = "1.44.2" version = "1.44.2"
@ -658,30 +523,12 @@ dependencies = [
"once_cell", "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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -766,8 +613,6 @@ name = "yyyi_ru"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"axum", "axum",
"json5",
"mtgott", "mtgott",
"serde_json",
"tokio", "tokio",
] ]

View File

@ -1,10 +1,9 @@
[package] [package]
name = "yyyi_ru" name = "yyyi_ru"
version = "0.1.0" version = "0.1.0"
edition = "2024"
[dependencies] [dependencies]
axum = "0.8.3" axum = "0.8.3"
tokio = { version = "1.44.1", features = ["rt-multi-thread"] } tokio = { version = "1.44.1", features = ["rt-multi-thread"] }
json5 = "0.4.1"
serde_json = "1.0.140"
mtgott = { path = "./mtgott" } mtgott = { path = "./mtgott" }

View File

@ -1,6 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{% import "lang_macro.html" as lang_macro %}
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@ -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));
}

View File

@ -12,7 +12,7 @@ use std::rc::Rc;
pub fn search_dir_rec_helper( pub fn search_dir_rec_helper(
res: &mut Vec<Vec<String>>, res: &mut Vec<Vec<String>>,
fs_path: PathBuf, virtual_path: String, 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<dyn Error>> { )-> Result<(), Box<dyn Error>> {
if recc == 0 { return Err("Recursion limit exceeded".into()); } if recc == 0 { return Err("Recursion limit exceeded".into()); }
let fs_path = canonicalize(fs_path)?; let fs_path = canonicalize(fs_path)?;
@ -45,65 +45,87 @@ pub fn search_dir_rec_helper(
} }
pub fn search_dir( 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<Vec<Vec<String>>, Box<dyn Error>> { -> Result<Vec<Vec<String>>, Box<dyn Error>> {
let mut res: Vec<Vec<String>> = (0..allowed_extensions.len()).map(|_| Vec::new()).collect(); let mut res: Vec<Vec<String>> = (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)?; search_dir_rec_helper(&mut res, PathBuf::from(p), String::new(), allowed_extensions, is_valid_name, 100)?;
Ok(res) Ok(res)
} }
struct MtgottDirContent { pub struct MtgottDirFiles {
mtgott: Vec<String>, pub mtgott: Vec<String>,
imtgott: Vec<String>, pub imtgott: Vec<String>,
plain: Vec<String> pub plain: Vec<String>
} }
pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result<MtgottDirContent, Box<dyn Error>> { pub fn search_mtgott_dir(p: &str, plain_ext: &str) -> Result<MtgottDirFiles, Box<dyn Error>> {
let mut all = search_dir(p, &[ let mut all = search_dir(p, &[
&(String::from(".mtgott") + plain_ext), &(String::from(".mtgott") + plain_ext),
&(String::from(".imtgott") + plain_ext), &(String::from(".imtgott") + plain_ext),
plain_ext plain_ext
], is_special_name)?; ], &(|s| !is_special_name(s)))?;
Ok(MtgottDirContent{ Ok(MtgottDirFiles {
mtgott: std::mem::take(&mut all[0]), mtgott: std::mem::take(&mut all[0]),
imtgott: std::mem::take(&mut all[1]), imtgott: std::mem::take(&mut all[1]),
plain: std::mem::take(&mut all[2]), plain: std::mem::take(&mut all[2]),
}) })
} }
pub fn get_all_templates(p: &str, plain_ext: &str) -> Result<MTGOTT, Box<dyn Error>> { pub struct FileWithPath {
let source = search_mtgott_dir(p, plain_ext)?; pub v_path: String,
pub text: String,
}
pub struct MtgottDirContent {
pub mtgott: Vec<FileWithPath>,
pub imtgott: Vec<FileWithPath>,
pub plain: Vec<FileWithPath>,
}
fn read_file_vector(p: &str, plain_ext: &str, secondary_ext: &str, files: Vec<String>) -> Result<Vec<FileWithPath>, Box<dyn Error>> {
files.iter().map(|v_path| -> Result<FileWithPath, Box<dyn Error>> {
Ok(FileWithPath{
v_path: v_path.clone(),
text: String::from_utf8(fs::read(
&format!("{p}/{v_path}{secondary_ext}{plain_ext}")
)?)?})
}).collect::<Result<Vec<FileWithPath>, Box<dyn Error>>>()
}
pub fn read_mtgott_dir_content(p: &str, plain_ext: &str) -> Result<MtgottDirContent, Box<dyn Error>> {
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<MTGOTT, Box<dyn Error>> {
let mut res = MTGOTT{root: SharedValue::Dict(HashMap::new()), source_expressions: Vec::new(), source_elements: Vec::new()}; let mut res = MTGOTT{root: SharedValue::Dict(HashMap::new()), source_expressions: Vec::new(), source_elements: Vec::new()};
for cut_path in source.mtgott { for FileWithPath{v_path, text} in dir.mtgott {
let path = format!("{cut_path}.mtgott{plain_ext}"); let path = format!("{v_path}.mtgott{}", plain_ext);
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let plemege = parse_one_file_packed(&text, &v_path, &mut res.source_expressions)?;
let text = std::str::from_utf8(&text_bytes)?;
let plemege = parse_one_file_packed(text, &cut_path, &mut res.source_expressions)?;
let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?; 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 { for FileWithPath{v_path, text} in dir.imtgott {
let path = format!("{cut_path}.imtgott{plain_ext}"); let path = format!("{v_path}.imtgott{}", plain_ext);
let text_bytes = fs::read(PathBuf::from(p).join(&path))?; let plemege = parse_one_file_simplified(&text, &v_path, &mut res.source_expressions)?;
let text = std::str::from_utf8(&text_bytes)?;
let plemege = parse_one_file_simplified(text, &cut_path, &mut res.source_expressions)?;
let compiled = plemege_to_value(plemege, &path, &mut res.source_elements, &mut res.source_expressions, 150)?; 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 { for FileWithPath{v_path, text} in dir.plain {
let path = format!("{cut_path}{plain_ext}"); add_path_with_slashes_to_root(&mut res.root, &v_path, SharedValue::Str(text))?
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))?
} }
Ok(res) Ok(res)
} }
pub fn get_all_templates_plus_builtins( pub fn get_all_templates_plus_builtins_from_dir_text(
p: &str, plain_ext: &str, dir: MtgottDirContent, plain_ext: &str,
sanitize: fn(&str) -> String, sanitize: fn(&str) -> String,
) -> Result<MTGOTT, Box<dyn Error>>{ ) -> Result<MTGOTT, Box<dyn Error>>{
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( 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 /* 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*/ * 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) Ok(res)
} }
pub fn get_root_html(p: &str) -> Result<MTGOTT, Box<dyn Error>>{ pub fn get_root_html_from_dir_text_html(dir: MtgottDirContent) -> Result<MTGOTT, Box<dyn Error>>{
get_all_templates_plus_builtins(p, ".html", escape_for_html) get_all_templates_plus_builtins_from_dir_text(dir, ".html", escape_for_html)
}
pub fn get_root_html(p: &str) -> Result<MTGOTT, Box<dyn Error>> {
get_root_html_from_dir_text_html(read_mtgott_dir_content(p, ".html")?)
} }

View File

@ -251,13 +251,13 @@ impl<'a> Parser<'a> {
return Err(self.new_unexpected_char_error(expected_pack_name)) return Err(self.new_unexpected_char_error(expected_pack_name))
} }
let child_name: &str = &self.text[p1..self.p]; 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)) return Err(FileParsingError::new(illegal_pack_name, p1, self.p))
} }
if let Some(_) = res.get(child_name) { if let Some(_) = res.get(child_name) {
return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p))
} }
self.skip_normal_word(); self.skip_whitespace();
if self.is_ahead("$}"){ if self.is_ahead("$}"){
self.p += 2; self.p += 2;
@ -471,7 +471,7 @@ impl<'a> Parser<'a> {
return finishing_touches(ReasonOfElementEnd{p1: self.p, cmd: BlockEndingTag::EOF}, res); return finishing_touches(ReasonOfElementEnd{p1: self.p, cmd: BlockEndingTag::EOF}, res);
} else if self.is_ahead("{{") { } else if self.is_ahead("{{") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
self.skip_whitespace(); self.p += 2;
if !self.is_ahead("}}") { if !self.is_ahead("}}") {
let expr: Expression = self.parse_expression( let expr: Expression = self.parse_expression(
arg_names, &mut (0..arg_names.len()).collect(), recc)?; arg_names, &mut (0..arg_names.len()).collect(), recc)?;
@ -488,7 +488,7 @@ impl<'a> Parser<'a> {
tp1 = self.p; tp1 = self.p;
} else if self.is_ahead("{[") { } else if self.is_ahead("{[") {
fin_static(self, tp1, &mut res); fin_static(self, tp1, &mut res);
self.skip_whitespace(); self.p += 2;
let expr: Expression = self.parse_expression( let expr: Expression = self.parse_expression(
arg_names, &mut (0..arg_names.len()).collect(), recc)?; arg_names, &mut (0..arg_names.len()).collect(), recc)?;
res.push(SubElement::InsertExpr(expr)); res.push(SubElement::InsertExpr(expr));
@ -750,7 +750,9 @@ impl<'a> Parser<'a> {
) -> Result<Option<Expression>, FileParsingError> { ) -> Result<Option<Expression>, FileParsingError> {
self.check_recursion_limit(recc)?; self.check_recursion_limit(recc)?;
self.skip_whitespace(); 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; self.p += 1;
let expr = self.parse_expression(arg_names, used_local_consts, recc - 1)?; let expr = self.parse_expression(arg_names, used_local_consts, recc - 1)?;
self.skip_whitespace(); self.skip_whitespace();

125
mtgott/tests/april_24.rs Normal file
View File

@ -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");
}

View File

@ -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<u8>
}
struct AssetsCache {
static_assets: HashMap<String, StaticAsset>,
pages: MTGOTT,
}
struct ExtensionContentTypeCorr{
extension: &'static str,
content_type: HeaderValue,
}
fn load_static_assets(p: &str, need: &[ExtensionContentTypeCorr]) -> Result<HashMap<String, StaticAsset>, Box<dyn Error>> {
let e: Vec<&'static str> = need.iter().map(|corr: &ExtensionContentTypeCorr| corr.extension).collect();
let content = search_dir(p, &e, &(|_| true))?;
let mut st: HashMap<String, StaticAsset> = 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<HashMap<String, StaticAsset>, Box<dyn Error>> {
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<AssetsCache, Box<dyn Error>> {
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<String>,
axum::extract::State(assets): axum::extract::State<Arc<AssetsCache>>,
) -> Result<([(axum::http::HeaderName, axum::http::HeaderValue); 1], Vec<u8>), 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<Arc<AssetsCache>>,
// axum::extract::Extension(t): axum::extract::Extension<tera::Tera>
) -> axum::response::Html<String> {
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<Arc<AssetsCache>>,
// axum::extract::Extension(t): axum::extract::Extension<tera::Tera>
) -> 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();
}

View File

@ -1,6 +1,6 @@
use yyyi_ru::yyyi_ru::MAIN; use yyyi_ru::run_yyyi_ru;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
MAIN().await run_yyyi_ru().await
} }

View File

@ -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<u8>
}
#[derive(Clone)]
struct AssetsCache {
// templates: tera::Tera,
static_assets: HashMap<String, StaticAsset>,
missing_text: Vec<(String, serde_json::Value)>
}
struct ExtensionContentTypeCorr{
extension: &'static str,
content_type: HeaderValue,
}
fn read_static_html_page(p: &Path) -> io::Result<StaticAsset> {
Ok(StaticAsset {
content_type: HeaderValue::from_str("text/html").unwrap(),
content: fs::read(p)?
})
}
fn load_static_assets(p: &Path, need: &[ExtensionContentTypeCorr]) -> io::Result<HashMap<String, StaticAsset>> {
if !p.is_dir() {
return Err(io::Error::from(io::ErrorKind::NotADirectory))
}
let mut st: HashMap<String, StaticAsset> = HashMap::new();
let mut td: Vec<String> = 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<HashMap<String, StaticAsset>> {
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<Vec<(String, serde_json::Value)>, Box<dyn std::error::Error>> {
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<String>,
axum::extract::State(assets): axum::extract::State<Arc<AssetsCache>>,
) -> Result<([(axum::http::HeaderName, axum::http::HeaderValue); 1], Vec<u8>), 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<Arc<AssetsCache>>,
// axum::extract::Extension(t): axum::extract::Extension<tera::Tera>
) -> axum::response::Html<String> {
// 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<Arc<AssetsCache>>,
// axum::extract::Extension(t): axum::extract::Extension<tera::Tera>
) -> 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();
}