use anyhow::Result; use asciidoctrine::{self, *}; use clap::Parser; use lisi::*; use pretty_assertions::assert_eq; #[test] fn indented_snippets_with_params() -> Result<()> { let content = r#" We have a snippet [[inner_snippet]] [source, yaml] ---- this is my snippet <> ---- And we indent it [source, yaml, save] .sample.yaml ---- category: <> category2: <> >> ---- And here we have a param we reference [[referenced_param]] [source, yaml] ---- referenced param text ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample.yaml").unwrap(), r#"category: this is my snippet my param text category2: this is my snippet referenced param text "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn deep_nested_snippets_with_params() -> Result<()> { let content = r#" We have a basic snippet [[snippet_with_param]] [source, python] ---- print("<>") ---- And we have a snippet that uses it. [[calling_snippet_one]] [source, python] ---- def my_function(): <> # <> ---- We have onther snippet that uses it too. But it overwrites the parameter. [[calling_snippet_two]] [source, python] ---- def my_other_function(): <> >> # <> ---- When we use these snippets we expect them to do different things. The first should have the nested inner snippet untouched and the second should use the parameter in the nested snippet. [source, python, save] .nested.py ---- <> <> ---- The default text is [[echo_text]]`untouched`. "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("nested.py").unwrap(), r#"def my_function(): print("untouched") # touch only outer def my_other_function(): print("touch inner snippet too") # touch inner snippet too "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn snippets_with_nested_params() -> Result<()> { let content = r#" We have a snippet [[inner_snippet]] [source, yaml] ---- this is my snippet <> ---- And we indent it [[category_template]] [source, yaml] ---- category: <> category2: <> ---- Normally our `value_category2` is like this [[value_category2]] [source, yaml] ---- <> >> ---- And here we have a param we reference [[referenced_param]] [source, yaml] ---- referenced param text ---- But in the end we want to use our `category_template` twice. Once in the normal way and once with a substtuted inner snippet. [source, yaml, save] .sample.yaml ---- <> <> >> ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample.yaml").unwrap(), r#"category: this is my snippet my param text category2: this is my snippet referenced param text category: this is my snippet my param text category2: this is my snippet substituted with a param inside a param "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn use_snippets() -> Result<()> { let content = r#" We need the testmodule for this project. [[sample1_required_modules]] [source, lua] ---- require "testmodule" ---- This is the importing file. We could print out the version. [source, lua, save] .sample1.lua ---- <> print(testmodule.version) ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample1.lua").unwrap(), r#"require "testmodule" print(testmodule.version) "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn handle_snippet_order() -> Result<()> { let content = r#" First we give a short outline of the program. It imports the required modules and then prints out its version. [source, lua, save] .sample2.lua ---- <> print(testmodule.version) ---- We need the testmodule for this project. [[sample2_required_modules]] [source, lua] ---- require "testmodule" ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample2.lua").unwrap(), r#"require "testmodule" print(testmodule.version) "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn use_snippet_multiple_times() -> Result<()> { let content = r#" Lets assume we want to use the following snippet in multiple places. [[sample3_multiple]] [source, lua] ---- require "testmodule" ---- Than we could import it in the same snippet multiple times. [source, lua, save] .sample3-1.lua ---- <> print(testmodule.version) <> ---- And we could even use it again in another snippet. [source, lua, save] .sample3-2.lua ---- <> print(testmodule.version .. "my other snippet") ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample3-1.lua").unwrap(), r#"require "testmodule" print(testmodule.version) require "testmodule" "# ); assert_eq!( outputs.remove("sample3-2.lua").unwrap(), r#"require "testmodule" print(testmodule.version .. "my other snippet") "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn append_snippets() -> Result<()> { let content = r#" We do some thing in our code. [source, lua, save] .sample4.lua ---- <> print(result_of_someprocess) ---- To do this we need to do something with a variable. [[some_process]] [source, lua] ---- variable = 42 variable = variable + 42 ---- But something else has also to be done. For example we need to set the result. [[some_process]] [source, lua] ---- result_of_someprocess = variable * variable ---- Now lets go on to another thing ... "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample4.lua").unwrap(), r#"variable = 42 variable = variable + 42 result_of_someprocess = variable * variable print(result_of_someprocess) "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn append_snippets_with_customized_join() -> Result<()> { let content = r#" Let's imagine we need some rust struct. [[mystruct]] [source, rust] ---- pub struct MyStruct { <> } ---- In our main process we need to define the struct and initialize it. [source, rust, save] .sample5.rs ---- <> impl MyStruct { pub fn new { MyStruct { <> } } } ---- In our struct we have variable x [[mystruct_fields]] [source, rust] ---- x: String ---- And we initialize it properly [[init_fields]] [source, rust] ---- x: "this is the x text".to_string() ---- Now we can talk about all the functions that use x... After some time we may have a function that use some other variable y. [[mystruct_fields]] [source, rust] ---- y: u8 ---- And how is it initialized? You know the answer: [[init_fields]] [source, rust] ---- y: 42 ---- And so on ... "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample5.rs").unwrap(), r#"pub struct MyStruct { x: String, y: u8 } impl MyStruct { pub fn new { MyStruct { x: "this is the x text".to_string(), y: 42 } } } "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn use_inline_snippets() -> Result<()> { let content = r#" Let's imagine we need some rust struct. [[mystruct]] [source, rust] ---- pub struct MyStruct { <> } ---- In our main process we need to define the struct and initialize it. [source, rust, save] .sample5.rs ---- <> impl MyStruct { pub fn new { MyStruct { <> } } } ---- In our struct we have variable [[mystruct_fields]]`x: String`. And we initialize it properly [[init_fields]] [source, rust] ---- x: "this is the x text".to_string() ---- Now we can talk about all the functions that use x... After some time we may have a function that use some other variable [[mystruct_fields]]`y: u8`. And how is it initialized? You know the answer: [[init_fields]] [source, rust] ---- y: 42 ---- And so on ... "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample5.rs").unwrap(), r#"pub struct MyStruct { x: String, y: u8 } impl MyStruct { pub fn new { MyStruct { x: "this is the x text".to_string(), y: 42 } } } "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn indented_snippets() -> Result<()> { let content = r#" Imagine you want to print a long pattern of "//***//" around so text to emphasize it. We can do this: [[print_pattern_once]] [source, c] ---- printf("/"); printf("***"); printf("/"); ---- But we want this line to be long [[print_pattern]] [source, c] ---- for (i=0;i<5;i++) { <> } print("\n"); ---- And now lets emphasize the text. [source, c, save] .sample7.c ---- <> print("My emphasized text!!\n" <> ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample7.c").unwrap(), r#"for (i=0;i<5;i++) { printf("/"); printf("***"); printf("/"); } print("\n"); print("My emphasized text!!\n" for (i=0;i<5;i++) { printf("/"); printf("***"); printf("/"); } print("\n"); "# ); assert!(outputs.is_empty()); Ok(()) } #[test] fn snippets_with_params() -> Result<()> { let content = r#" There is a snippet we want to use in different contexts. [[test_condition]] [source, sh] ---- if [[ <> ]] then echo "<> >>" exit <> fi ---- Normally we exit with the message [[err_message]]`the condition <> was not met` and return exit code [[exit_code]]`1`. Now we can use this snippet to test some condition before we execute our script. Lets say we want to make sure `file_xy.txt` exists. [[checks]] [source, sh] ---- <> ---- But we could also override the default snippets with a custom one. For example to change the error message. [[checks]] [source, sh] ---- <> ---- It's also possible to nest snippets. We can just reference them. Let's say we would like to return the message [[custom_err_message]]`return from nested param snippet with code <>` and exit code [[custom_exit_code]]`42`. [[checks]] [source, sh] ---- <>, exit_code:=<>>> ---- What about more deeply nested snippets? Let's say we have another condition where we want something to be done when it is met instead of finishing the program. [[process_condition]] [source, sh] ---- if [[ <> ]] then echo "<> >>" <> fi ---- As the default info we put out [[info_message]]`the condition <> was met` and call a function [[do_something]]`myfunc($1)`. [[checks]] [source, sh] ---- <> ---- But now suppose we want to process a certain function if `$2` exists but when [[inner_condition]]`-f $1` matches too we want to do something additionally. In this case we have to nest our snippets at a deeper level. [[checks]] [source, sh] ---- <> >> ---- [[deep_nested_snippet]] [source, sh] ---- <> <>, do_something:=<> >> ---- Now we put all of these conditions at the beginning of our script. [source, sh, save] .sample8.sh ---- <> echo "you passed all checks" ---- "#; let reader = AsciidocReader::new(); let opts = options::Opts::parse_from(vec![""].into_iter()); let mut env = util::Env::Cache(util::Cache::new()); let ast = reader.parse(content, &opts, &mut env)?; let mut lisi = Lisi::from_env(env); let _ast = lisi.transform(ast)?; // TODO ast vergleichen let mut outputs = lisi.into_cache().unwrap(); assert_eq!( outputs.remove("sample8.sh").unwrap(), r#"if [[ -f file_xy.txt ]] then echo "the condition -f file_xy.txt was not met" exit 1 fi if [[ -f file_yz.txt ]] then echo "my custom err message yz" exit 1 fi if [[ -f nested_params.txt ]] then echo "return from nested param snippet with code 42" exit 42 fi if [[ -f $1 ]] then echo "the condition -f $1 was met" myfunc($1) fi if [[ -f $2 ]] then echo "the condition -f $2 was met" if [[ test_default_condition($2) ]] then echo "the condition test_default_condition($2) was met" myfunc($1) fi if [[ -f $1 ]] then echo "the condition -f $1 was met" nestedfunc($1, $2) fi fi echo "you passed all checks" "# ); assert!(outputs.is_empty()); Ok(()) }