From 19b042d7b4f6dfe0870aa800254d4d25189b7e5b Mon Sep 17 00:00:00 2001 From: Barak Nehmad Date: Thu, 21 May 2020 17:22:56 +0300 Subject: [PATCH 1/8] Changed Rust project structure, and started the new graph generation script. --- .../.gitignore | 0 .../Cargo.toml | 8 ++- .../src/bin/generate-levels-graph.rs | 60 +++++++++++++++++++ .../src/bin/generate-pre-receive-hook.rs} | 17 +----- .../make-git-better-scripts/src/lib/lib.rs | 15 +++++ .../template.tmpl | 0 6 files changed, 83 insertions(+), 17 deletions(-) rename scripts/{generate-pre-receive-hook => make-git-better-scripts}/.gitignore (100%) rename scripts/{generate-pre-receive-hook => make-git-better-scripts}/Cargo.toml (78%) create mode 100644 scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs rename scripts/{generate-pre-receive-hook/src/main.rs => make-git-better-scripts/src/bin/generate-pre-receive-hook.rs} (91%) create mode 100644 scripts/make-git-better-scripts/src/lib/lib.rs rename scripts/{generate-pre-receive-hook => make-git-better-scripts}/template.tmpl (100%) diff --git a/scripts/generate-pre-receive-hook/.gitignore b/scripts/make-git-better-scripts/.gitignore similarity index 100% rename from scripts/generate-pre-receive-hook/.gitignore rename to scripts/make-git-better-scripts/.gitignore diff --git a/scripts/generate-pre-receive-hook/Cargo.toml b/scripts/make-git-better-scripts/Cargo.toml similarity index 78% rename from scripts/generate-pre-receive-hook/Cargo.toml rename to scripts/make-git-better-scripts/Cargo.toml index 7e1c9db..0c791f3 100644 --- a/scripts/generate-pre-receive-hook/Cargo.toml +++ b/scripts/make-git-better-scripts/Cargo.toml @@ -1,11 +1,15 @@ [package] -name = "generate-pre-receive-hook" +name = "make-git-better-scripts" version = "0.1.0" authors = ["Shay Nehmad "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "common" +path = "src/lib/lib.rs" + [dependencies] structopt = "0.3.13" serde = { version = "1.0", features = ["derive"] } @@ -13,4 +17,4 @@ toml = "0.5" tinytemplate = "1.0.4" simple_logger = "1.6.0" log = "0.4" - +petgraph = "0.5" diff --git a/scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs b/scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs new file mode 100644 index 0000000..5ad0144 --- /dev/null +++ b/scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs @@ -0,0 +1,60 @@ +use structopt::StructOpt; +use std::fs; +use log::{debug, info}; +use petgraph::{Graph, Directed}; +use petgraph::graph::NodeIndex; + +use common::{GameConfig, Level}; + +pub type LevelsGraph = Graph<&'static Level, &'static Level, Directed>; +pub type Nodes = Vec::<&'static NodeIndex>; + +#[derive(Debug, StructOpt)] +#[structopt(about = "A script to generate a levels graph from a game config.")] +struct Cli { + #[structopt(parse(from_os_str), help = "Path to game config file to read")] + game_config_path: std::path::PathBuf, + + #[structopt( + short = "v", + long = "verbose", + help = "Show more information about the actions taken" + )] + verbose: bool, +} + +fn add_level_nodes_to_graph(current_level: Level, graph: &Graph<&Level, &Level>, game_config: &GameConfig) -> Result { + let mut new_nodes = Vec::<&NodeIndex>::new(); + if current_level.flags.len() == 0 { + Ok(new_nodes) + } + for flag in current_level.flags { + + } +} + +fn create_graph_from_game_config(game_config: &GameConfig) -> Result, &str> { + let mut levels_graph = Graph::<&Level, &Level>::new(); + + + for level in &game_config.levels { + //let level_node = levels_graph.add_node(level); + + } + + Ok(levels_graph) +} + +fn main() { + let args = Cli::from_args(); + + if args.verbose { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + } else { + simple_logger::init_with_level(log::Level::Info).unwrap(); + }; + + info!("Reading script from {:?}", args.game_config_path); + let game_config_file_contents = fs::read_to_string(args.game_config_path).unwrap(); + +} \ No newline at end of file diff --git a/scripts/generate-pre-receive-hook/src/main.rs b/scripts/make-git-better-scripts/src/bin/generate-pre-receive-hook.rs similarity index 91% rename from scripts/generate-pre-receive-hook/src/main.rs rename to scripts/make-git-better-scripts/src/bin/generate-pre-receive-hook.rs index f2034e1..ca42a16 100644 --- a/scripts/generate-pre-receive-hook/src/main.rs +++ b/scripts/make-git-better-scripts/src/bin/generate-pre-receive-hook.rs @@ -1,6 +1,4 @@ -use log; use log::{debug, info}; -use serde::{Deserialize, Serialize}; use simple_logger; use std::fs; use std::io::Write; @@ -8,6 +6,8 @@ use structopt::StructOpt; use tinytemplate::TinyTemplate; use toml; +use common::{GameConfig}; + #[derive(Debug, StructOpt)] #[structopt(about = "A script to generate the master pre-receive hook file.")] struct Cli { @@ -32,19 +32,6 @@ struct Cli { verbose: bool, } -#[derive(Debug, Clone, Deserialize, Serialize)] -struct Level { - title: String, - branch: String, - solution_checker: String, - flags: Vec, -} - -#[derive(Debug, Deserialize, Serialize)] -struct GameConfig { - levels: Vec, -} - fn replace_flags_with_branch_names(game_config: &mut GameConfig) { let levels_info = game_config.levels.clone(); diff --git a/scripts/make-git-better-scripts/src/lib/lib.rs b/scripts/make-git-better-scripts/src/lib/lib.rs new file mode 100644 index 0000000..beb85f1 --- /dev/null +++ b/scripts/make-git-better-scripts/src/lib/lib.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; +use log::debug; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Level { + pub title: String, + pub branch: String, + pub solution_checker: String, + pub flags: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct GameConfig { + pub levels: Vec, +} diff --git a/scripts/generate-pre-receive-hook/template.tmpl b/scripts/make-git-better-scripts/template.tmpl similarity index 100% rename from scripts/generate-pre-receive-hook/template.tmpl rename to scripts/make-git-better-scripts/template.tmpl From 994b62b96108b397355fa5002684c9bb6065af02 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 25 May 2020 03:17:10 +0300 Subject: [PATCH 2/8] Moved the rust scripts one dir up and added some code to the generate script --- .../{make-git-better-scripts => }/.gitignore | 0 .../{make-git-better-scripts => }/Cargo.toml | 0 .../src/bin/generate-levels-graph.rs | 60 ----------- scripts/src/bin/generate-levels-graph.rs | 99 +++++++++++++++++++ .../src/bin/generate-pre-receive-hook.rs | 2 +- .../src/lib/lib.rs | 5 +- .../template.tmpl | 0 7 files changed, 102 insertions(+), 64 deletions(-) rename scripts/{make-git-better-scripts => }/.gitignore (100%) rename scripts/{make-git-better-scripts => }/Cargo.toml (100%) delete mode 100644 scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs create mode 100644 scripts/src/bin/generate-levels-graph.rs rename scripts/{make-git-better-scripts => }/src/bin/generate-pre-receive-hook.rs (99%) rename scripts/{make-git-better-scripts => }/src/lib/lib.rs (67%) rename scripts/{make-git-better-scripts => }/template.tmpl (100%) diff --git a/scripts/make-git-better-scripts/.gitignore b/scripts/.gitignore similarity index 100% rename from scripts/make-git-better-scripts/.gitignore rename to scripts/.gitignore diff --git a/scripts/make-git-better-scripts/Cargo.toml b/scripts/Cargo.toml similarity index 100% rename from scripts/make-git-better-scripts/Cargo.toml rename to scripts/Cargo.toml diff --git a/scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs b/scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs deleted file mode 100644 index 5ad0144..0000000 --- a/scripts/make-git-better-scripts/src/bin/generate-levels-graph.rs +++ /dev/null @@ -1,60 +0,0 @@ -use structopt::StructOpt; -use std::fs; -use log::{debug, info}; -use petgraph::{Graph, Directed}; -use petgraph::graph::NodeIndex; - -use common::{GameConfig, Level}; - -pub type LevelsGraph = Graph<&'static Level, &'static Level, Directed>; -pub type Nodes = Vec::<&'static NodeIndex>; - -#[derive(Debug, StructOpt)] -#[structopt(about = "A script to generate a levels graph from a game config.")] -struct Cli { - #[structopt(parse(from_os_str), help = "Path to game config file to read")] - game_config_path: std::path::PathBuf, - - #[structopt( - short = "v", - long = "verbose", - help = "Show more information about the actions taken" - )] - verbose: bool, -} - -fn add_level_nodes_to_graph(current_level: Level, graph: &Graph<&Level, &Level>, game_config: &GameConfig) -> Result { - let mut new_nodes = Vec::<&NodeIndex>::new(); - if current_level.flags.len() == 0 { - Ok(new_nodes) - } - for flag in current_level.flags { - - } -} - -fn create_graph_from_game_config(game_config: &GameConfig) -> Result, &str> { - let mut levels_graph = Graph::<&Level, &Level>::new(); - - - for level in &game_config.levels { - //let level_node = levels_graph.add_node(level); - - } - - Ok(levels_graph) -} - -fn main() { - let args = Cli::from_args(); - - if args.verbose { - simple_logger::init_with_level(log::Level::Debug).unwrap(); - } else { - simple_logger::init_with_level(log::Level::Info).unwrap(); - }; - - info!("Reading script from {:?}", args.game_config_path); - let game_config_file_contents = fs::read_to_string(args.game_config_path).unwrap(); - -} \ No newline at end of file diff --git a/scripts/src/bin/generate-levels-graph.rs b/scripts/src/bin/generate-levels-graph.rs new file mode 100644 index 0000000..3147cc5 --- /dev/null +++ b/scripts/src/bin/generate-levels-graph.rs @@ -0,0 +1,99 @@ +use log::{debug, info}; +use petgraph::graph::NodeIndex; +use petgraph::{Directed, Graph}; +use std::fs; +use structopt::StructOpt; + +use common::{GameConfig, Level}; + +type LevelsGraph<'a> = Graph<&'a Level, (), Directed>; +type Nodes<'a> = Vec<&'a NodeIndex>; + +#[derive(Debug, StructOpt)] +#[structopt(about = "A script to generate a levels graph from a game config.")] +struct Cli { + #[structopt(parse(from_os_str), help = "Path to game config file to read")] + game_config_path: std::path::PathBuf, + + #[structopt( + short = "v", + long = "verbose", + help = "Show more information about the actions taken" + )] + verbose: bool, +} + +/// Recursive function that populates the game graph.NodeIndex +/// +/// If receives a graph initialized with the first level as a root node. +fn add_level_nodes_to_graph<'a, 'gl>( + current_level: Level, + current_node: &'gl NodeIndex, + graph: &'gl mut LevelsGraph, + game_config: &'a GameConfig, +) -> Nodes<'gl> { + let mut new_nodes = Nodes::new(); + if current_level.flags.len() == 0 { + return new_nodes; + }; + + for flag in current_level.flags { + debug!("level {} flag {}", current_level.title, flag); + let levels_iterator = game_config.levels.iter(); + let found = levels_iterator.find(|x| x.title == flag); + match found { + Some(x) => { + debug!("The flag does point to another level, {}. Adding level as node to graph", x.title); + // What's the issue here? + let new_node = graph.add_node(x); + new_nodes.push(&new_node); + debug!("Adding edge from {} to {}", current_level.title, x.title); + graph.add_edge(*current_node, new_node, ()); + debug!("Recursive calling add nodes on {}", x.title); + let sub_nodes = add_level_nodes_to_graph( + *x, + &new_node, + graph, + &game_config); + // new_nodes.append(sub_nodes); + } + None => { + debug!("The flag doesn't point to another level - no need to recurse"); + } + } + }; + return new_nodes; +} + +fn create_graph_from_game_config(game_config: &GameConfig) -> LevelsGraph { + let mut levels_graph = LevelsGraph::new(); + + let first_level = game_config.levels[0]; + let tree_root = levels_graph.add_node(&first_level); + let nodes = add_level_nodes_to_graph( + first_level, + &tree_root, + &mut levels_graph, + &game_config + ); + + return levels_graph; +} + +fn main() { + let args = Cli::from_args(); + + if args.verbose { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + } else { + simple_logger::init_with_level(log::Level::Info).unwrap(); + }; + + info!("Reading script from {:?}", args.game_config_path); + let game_config_file_contents = fs::read_to_string(args.game_config_path).unwrap(); + let game_config: GameConfig = toml::from_str(&game_config_file_contents).unwrap(); + + let levels_graph = create_graph_from_game_config(&game_config); + + println!("{:?}", levels_graph); +} diff --git a/scripts/make-git-better-scripts/src/bin/generate-pre-receive-hook.rs b/scripts/src/bin/generate-pre-receive-hook.rs similarity index 99% rename from scripts/make-git-better-scripts/src/bin/generate-pre-receive-hook.rs rename to scripts/src/bin/generate-pre-receive-hook.rs index ca42a16..6018620 100644 --- a/scripts/make-git-better-scripts/src/bin/generate-pre-receive-hook.rs +++ b/scripts/src/bin/generate-pre-receive-hook.rs @@ -6,7 +6,7 @@ use structopt::StructOpt; use tinytemplate::TinyTemplate; use toml; -use common::{GameConfig}; +use common::GameConfig; #[derive(Debug, StructOpt)] #[structopt(about = "A script to generate the master pre-receive hook file.")] diff --git a/scripts/make-git-better-scripts/src/lib/lib.rs b/scripts/src/lib/lib.rs similarity index 67% rename from scripts/make-git-better-scripts/src/lib/lib.rs rename to scripts/src/lib/lib.rs index beb85f1..0b57310 100644 --- a/scripts/make-git-better-scripts/src/lib/lib.rs +++ b/scripts/src/lib/lib.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; -use log::debug; -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct Level { pub title: String, pub branch: String, @@ -9,7 +8,7 @@ pub struct Level { pub flags: Vec, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct GameConfig { pub levels: Vec, } diff --git a/scripts/make-git-better-scripts/template.tmpl b/scripts/template.tmpl similarity index 100% rename from scripts/make-git-better-scripts/template.tmpl rename to scripts/template.tmpl From a85e08324f0e618b6c6dcec7c8509373610be797 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Mon, 25 May 2020 04:25:39 +0300 Subject: [PATCH 3/8] Works up until the graph is created --- levels/game-config.toml | 2 +- scripts/src/bin/generate-levels-graph.rs | 42 +++++++++++------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/levels/game-config.toml b/levels/game-config.toml index 0e92a65..f8bf416 100644 --- a/levels/game-config.toml +++ b/levels/game-config.toml @@ -2,7 +2,7 @@ title = "clone" branch = "master" solution_checker = "echo No pushing to master. Read the README file; exit 1" - flags = [] # ["start-here"], but it's implicit in the readme + flags = ["start-here"] # ["start-here"], but it's implicit in the readme [[levels]] title = "start-here" diff --git a/scripts/src/bin/generate-levels-graph.rs b/scripts/src/bin/generate-levels-graph.rs index 3147cc5..b9c5f9e 100644 --- a/scripts/src/bin/generate-levels-graph.rs +++ b/scripts/src/bin/generate-levels-graph.rs @@ -1,13 +1,13 @@ use log::{debug, info}; use petgraph::graph::NodeIndex; use petgraph::{Directed, Graph}; +use petgraph::dot::{Dot, Config}; use std::fs; use structopt::StructOpt; use common::{GameConfig, Level}; -type LevelsGraph<'a> = Graph<&'a Level, (), Directed>; -type Nodes<'a> = Vec<&'a NodeIndex>; +type LevelsGraph = Graph; #[derive(Debug, StructOpt)] #[structopt(about = "A script to generate a levels graph from a game config.")] @@ -23,61 +23,57 @@ struct Cli { verbose: bool, } -/// Recursive function that populates the game graph.NodeIndex +/// Recursive function that populates the game graph /// /// If receives a graph initialized with the first level as a root node. -fn add_level_nodes_to_graph<'a, 'gl>( +fn add_level_nodes_to_graph<'a>( current_level: Level, - current_node: &'gl NodeIndex, - graph: &'gl mut LevelsGraph, + current_node: &'a NodeIndex, + levels_graph: &'a mut LevelsGraph, game_config: &'a GameConfig, -) -> Nodes<'gl> { - let mut new_nodes = Nodes::new(); +) { if current_level.flags.len() == 0 { - return new_nodes; + return; }; for flag in current_level.flags { debug!("level {} flag {}", current_level.title, flag); - let levels_iterator = game_config.levels.iter(); + let mut levels_iterator = game_config.levels.iter(); let found = levels_iterator.find(|x| x.title == flag); match found { Some(x) => { debug!("The flag does point to another level, {}. Adding level as node to graph", x.title); // What's the issue here? - let new_node = graph.add_node(x); - new_nodes.push(&new_node); + let new_node = levels_graph.add_node(x.clone()); debug!("Adding edge from {} to {}", current_level.title, x.title); - graph.add_edge(*current_node, new_node, ()); + levels_graph.add_edge(*current_node, new_node, ()); debug!("Recursive calling add nodes on {}", x.title); - let sub_nodes = add_level_nodes_to_graph( - *x, + add_level_nodes_to_graph( + x.clone(), &new_node, - graph, + levels_graph, &game_config); - // new_nodes.append(sub_nodes); } None => { debug!("The flag doesn't point to another level - no need to recurse"); } } }; - return new_nodes; } fn create_graph_from_game_config(game_config: &GameConfig) -> LevelsGraph { let mut levels_graph = LevelsGraph::new(); - let first_level = game_config.levels[0]; - let tree_root = levels_graph.add_node(&first_level); - let nodes = add_level_nodes_to_graph( + let first_level = game_config.levels[0].clone(); + let tree_root = levels_graph.add_node(first_level.clone()); + add_level_nodes_to_graph( first_level, &tree_root, &mut levels_graph, &game_config ); - return levels_graph; + levels_graph } fn main() { @@ -95,5 +91,5 @@ fn main() { let levels_graph = create_graph_from_game_config(&game_config); - println!("{:?}", levels_graph); + debug!("Generated graph:\n{:?}", Dot::with_config(&levels_graph, &[Config::EdgeNoLabel])); } From 92c0cbd7fce68e062ea10c98e0ff5af370450b52 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 27 May 2020 23:42:11 +0300 Subject: [PATCH 4/8] Generate level graph works! Still missing some documentation and some UTs --- levels/game-config.toml | 2 +- scripts/Cargo.toml | 1 + scripts/src/bin/generate-levels-graph.rs | 74 ++++++++++++++----- scripts/src/bin/templates/graph.tmpl | 34 +++++++++ .../bin/templates/hook.tmpl} | 0 scripts/src/lib/lib.rs | 12 +++ 6 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 scripts/src/bin/templates/graph.tmpl rename scripts/{template.tmpl => src/bin/templates/hook.tmpl} (100%) diff --git a/levels/game-config.toml b/levels/game-config.toml index f8bf416..0db9f04 100644 --- a/levels/game-config.toml +++ b/levels/game-config.toml @@ -2,7 +2,7 @@ title = "clone" branch = "master" solution_checker = "echo No pushing to master. Read the README file; exit 1" - flags = ["start-here"] # ["start-here"], but it's implicit in the readme + flags = ["start-here"] [[levels]] title = "start-here" diff --git a/scripts/Cargo.toml b/scripts/Cargo.toml index 0c791f3..d2ac915 100644 --- a/scripts/Cargo.toml +++ b/scripts/Cargo.toml @@ -13,6 +13,7 @@ path = "src/lib/lib.rs" [dependencies] structopt = "0.3.13" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" toml = "0.5" tinytemplate = "1.0.4" simple_logger = "1.6.0" diff --git a/scripts/src/bin/generate-levels-graph.rs b/scripts/src/bin/generate-levels-graph.rs index b9c5f9e..f8de5ce 100644 --- a/scripts/src/bin/generate-levels-graph.rs +++ b/scripts/src/bin/generate-levels-graph.rs @@ -1,13 +1,16 @@ use log::{debug, info}; +use petgraph::dot::{Config, Dot}; use petgraph::graph::NodeIndex; use petgraph::{Directed, Graph}; -use petgraph::dot::{Dot, Config}; +use serde::Serialize; use std::fs; +use std::io::Write; use structopt::StructOpt; +use tinytemplate::TinyTemplate; use common::{GameConfig, Level}; -type LevelsGraph = Graph; +type LevelsGraph = Graph; #[derive(Debug, StructOpt)] #[structopt(about = "A script to generate a levels graph from a game config.")] @@ -15,6 +18,16 @@ struct Cli { #[structopt(parse(from_os_str), help = "Path to game config file to read")] game_config_path: std::path::PathBuf, + #[structopt(parse(from_os_str), help = "Path to the graph template file to read")] + template_path: std::path::PathBuf, + + #[structopt( + parse(from_os_str), + default_value = "output/levelgraph.html", + help = "Path to output file (creates if doesn't exist)" + )] + output_path: std::path::PathBuf, + #[structopt( short = "v", long = "verbose", @@ -24,7 +37,7 @@ struct Cli { } /// Recursive function that populates the game graph -/// +/// /// If receives a graph initialized with the first level as a root node. fn add_level_nodes_to_graph<'a>( current_level: Level, @@ -42,23 +55,21 @@ fn add_level_nodes_to_graph<'a>( let found = levels_iterator.find(|x| x.title == flag); match found { Some(x) => { - debug!("The flag does point to another level, {}. Adding level as node to graph", x.title); - // What's the issue here? + debug!( + "The flag does point to another level, {}. Adding level as node to graph", + x.title + ); let new_node = levels_graph.add_node(x.clone()); debug!("Adding edge from {} to {}", current_level.title, x.title); - levels_graph.add_edge(*current_node, new_node, ()); + levels_graph.add_edge(*current_node, new_node, 0); debug!("Recursive calling add nodes on {}", x.title); - add_level_nodes_to_graph( - x.clone(), - &new_node, - levels_graph, - &game_config); + add_level_nodes_to_graph(x.clone(), &new_node, levels_graph, &game_config); } None => { debug!("The flag doesn't point to another level - no need to recurse"); } } - }; + } } fn create_graph_from_game_config(game_config: &GameConfig) -> LevelsGraph { @@ -66,16 +77,16 @@ fn create_graph_from_game_config(game_config: &GameConfig) -> LevelsGraph { let first_level = game_config.levels[0].clone(); let tree_root = levels_graph.add_node(first_level.clone()); - add_level_nodes_to_graph( - first_level, - &tree_root, - &mut levels_graph, - &game_config - ); + add_level_nodes_to_graph(first_level, &tree_root, &mut levels_graph, &game_config); levels_graph } +#[derive(Serialize)] +struct Context { + levels_graph_as_dot: String, +} + fn main() { let args = Cli::from_args(); @@ -91,5 +102,30 @@ fn main() { let levels_graph = create_graph_from_game_config(&game_config); - debug!("Generated graph:\n{:?}", Dot::with_config(&levels_graph, &[Config::EdgeNoLabel])); + let levels_graph_as_dot = Dot::with_config(&levels_graph, &[Config::EdgeNoLabel]); + let context = Context { + levels_graph_as_dot: format!("{}", levels_graph_as_dot), + }; + + debug!("Generated graph:\n{:?}", levels_graph_as_dot); + + info!("Reading template from {:?}", args.template_path); + let template_file_contents = fs::read_to_string(args.template_path).unwrap(); + + let mut tt = TinyTemplate::new(); + let template_name = "levels_graph"; + tt.add_template(template_name, &template_file_contents) + .unwrap(); + let rendered = tt.render(template_name, &context).unwrap(); + + debug!("########## RENDERED TEMPLATE ##########"); + debug!("{}\n", rendered); + + let mut output_dir = args.output_path.clone(); + output_dir.pop(); + fs::create_dir_all(&output_dir).expect("Failed to create parent dir"); + let mut output_file = fs::File::create(&args.output_path).expect("Couldn't create file!"); + output_file.write_all(&rendered.as_bytes()).unwrap(); + + info!("Wrote rendered file to {:?}", args.output_path); } diff --git a/scripts/src/bin/templates/graph.tmpl b/scripts/src/bin/templates/graph.tmpl new file mode 100644 index 0000000..bac6cf0 --- /dev/null +++ b/scripts/src/bin/templates/graph.tmpl @@ -0,0 +1,34 @@ +
+ + diff --git a/scripts/template.tmpl b/scripts/src/bin/templates/hook.tmpl similarity index 100% rename from scripts/template.tmpl rename to scripts/src/bin/templates/hook.tmpl diff --git a/scripts/src/lib/lib.rs b/scripts/src/lib/lib.rs index 0b57310..ed90d75 100644 --- a/scripts/src/lib/lib.rs +++ b/scripts/src/lib/lib.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::fmt; #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct Level { @@ -8,6 +9,17 @@ pub struct Level { pub flags: Vec, } +impl fmt::Display for Level { + // This trait requires `fmt` with this exact signature. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Write strictly the first element into the supplied output + // stream: `f`. Returns `fmt::Result` which indicates whether the + // operation succeeded or failed. Note that `write!` uses syntax which + // is very similar to `println!`. + write!(f, "{}", self.title) + } +} + #[derive(Debug, Default, Deserialize, Serialize)] pub struct GameConfig { pub levels: Vec, From 077d730a635eed582194083b0037600ac90752a5 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 28 May 2020 00:36:34 +0300 Subject: [PATCH 5/8] Improve how the graph looks and behaves in the UI --- scripts/src/bin/templates/graph.tmpl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/scripts/src/bin/templates/graph.tmpl b/scripts/src/bin/templates/graph.tmpl index bac6cf0..07fddfa 100644 --- a/scripts/src/bin/templates/graph.tmpl +++ b/scripts/src/bin/templates/graph.tmpl @@ -15,9 +15,25 @@ var container = document.getElementById('mynetwork'); var options = \{ - layout: \{ + autoResize: true, + nodes: \{ + shape: "box", + shadow: true, + color: "#e8eef2", + font: "20px arial black" + }, + edges: \{ + color: "#e8eef2", + }, + physics: \{ + enabled: true, + solver: "hierarchicalRepulsion", + }, + layout: \{ hierarchical: \{ - direction: "LR" + direction: "LR", + levelSeperation: 70, + nodeSpacing: 33, } } }; From cc5b59c334df3fba366a9426978d91ff34cfa450 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 28 May 2020 00:55:46 +0300 Subject: [PATCH 6/8] Added some unit tests, run with `cargo test` --- scripts/src/bin/generate-levels-graph.rs | 32 ++++++++++++++++++++ scripts/src/bin/generate-pre-receive-hook.rs | 30 ++++++++++++++++++ scripts/src/lib/lib.rs | 16 ++++++++++ 3 files changed, 78 insertions(+) diff --git a/scripts/src/bin/generate-levels-graph.rs b/scripts/src/bin/generate-levels-graph.rs index f8de5ce..76f2540 100644 --- a/scripts/src/bin/generate-levels-graph.rs +++ b/scripts/src/bin/generate-levels-graph.rs @@ -1,4 +1,5 @@ use log::{debug, info}; +use petgraph::algo::is_cyclic_directed; use petgraph::dot::{Config, Dot}; use petgraph::graph::NodeIndex; use petgraph::{Directed, Graph}; @@ -82,6 +83,37 @@ fn create_graph_from_game_config(game_config: &GameConfig) -> LevelsGraph { levels_graph } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_graph_from_game_config() { + let first_level = Level { + title: String::from("first"), + branch: String::from("first"), + solution_checker: String::from("first"), + flags: vec!["second".to_string()], + }; + let second_level = Level { + title: String::from("second"), + branch: String::from("sec"), + solution_checker: String::from("sec"), + flags: vec!["another".to_string(), "asdf".to_string()], + }; + + let game_conf = GameConfig { + levels: vec![first_level, second_level], + }; + let graph = create_graph_from_game_config(&game_conf); + + assert_eq!(graph.node_count(), 2); + assert_eq!(graph.edge_count(), 1); + assert!(graph.is_directed()); + assert!(!is_cyclic_directed(&graph)); + } +} + #[derive(Serialize)] struct Context { levels_graph_as_dot: String, diff --git a/scripts/src/bin/generate-pre-receive-hook.rs b/scripts/src/bin/generate-pre-receive-hook.rs index 6018620..61d7fcf 100644 --- a/scripts/src/bin/generate-pre-receive-hook.rs +++ b/scripts/src/bin/generate-pre-receive-hook.rs @@ -56,6 +56,36 @@ fn replace_flags_with_branch_names(game_config: &mut GameConfig) { } } +#[cfg(test)] +mod tests { + use super::*; + use common::{GameConfig, Level}; + + #[test] + fn test_replace_flags_with_branch_names() { + let first_level = Level { + title: "a".to_string(), + branch: "a".to_string(), + solution_checker: "a".to_string(), + flags: vec!["second_level_title".to_string()], + }; + let second_level = Level { + title: "second_level_title".to_string(), + branch: "second_level_branch".to_string(), + solution_checker: "b".to_string(), + flags: vec!["c".to_string()], + }; + let mut game_conf = GameConfig { + levels: vec![first_level, second_level], + }; + replace_flags_with_branch_names(&mut game_conf); + assert_eq!( + game_conf.levels[0].flags[0], + "second_level_branch".to_string() + ); + } +} + fn main() { let args = Cli::from_args(); diff --git a/scripts/src/lib/lib.rs b/scripts/src/lib/lib.rs index ed90d75..16406df 100644 --- a/scripts/src/lib/lib.rs +++ b/scripts/src/lib/lib.rs @@ -24,3 +24,19 @@ impl fmt::Display for Level { pub struct GameConfig { pub levels: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_level_display() { + let level = Level { + title: "tit".to_string(), + branch: "bra".to_string(), + solution_checker: "sol".to_string(), + flags: vec!["fla".to_string()], + }; + assert_eq!(format!("{}", level), "tit".to_string()); + } +} From 602f0fdb8e48c84a84e8605e1caa1633cd00856d Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 28 May 2020 15:08:28 +0300 Subject: [PATCH 7/8] Fix CR comments --- levels/game-config.toml | 2 +- scripts/src/bin/generate-levels-graph.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/levels/game-config.toml b/levels/game-config.toml index 0db9f04..c17a1bd 100644 --- a/levels/game-config.toml +++ b/levels/game-config.toml @@ -2,7 +2,7 @@ title = "clone" branch = "master" solution_checker = "echo No pushing to master. Read the README file; exit 1" - flags = ["start-here"] + flags = ["start-here"] [[levels]] title = "start-here" diff --git a/scripts/src/bin/generate-levels-graph.rs b/scripts/src/bin/generate-levels-graph.rs index 76f2540..51af246 100644 --- a/scripts/src/bin/generate-levels-graph.rs +++ b/scripts/src/bin/generate-levels-graph.rs @@ -1,5 +1,4 @@ use log::{debug, info}; -use petgraph::algo::is_cyclic_directed; use petgraph::dot::{Config, Dot}; use petgraph::graph::NodeIndex; use petgraph::{Directed, Graph}; @@ -86,6 +85,7 @@ fn create_graph_from_game_config(game_config: &GameConfig) -> LevelsGraph { #[cfg(test)] mod tests { use super::*; + use petgraph::algo::is_cyclic_directed; #[test] fn test_create_graph_from_game_config() { From 5a603764081ec3293fea1effff6efd9ba17518d6 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 28 May 2020 17:43:11 +0300 Subject: [PATCH 8/8] Fixed CR comment :) --- scripts/src/bin/templates/graph.tmpl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/src/bin/templates/graph.tmpl b/scripts/src/bin/templates/graph.tmpl index 07fddfa..5cbcf91 100644 --- a/scripts/src/bin/templates/graph.tmpl +++ b/scripts/src/bin/templates/graph.tmpl @@ -32,7 +32,7 @@ layout: \{ hierarchical: \{ direction: "LR", - levelSeperation: 70, + levelSeparation: 100, nodeSpacing: 33, } } @@ -44,7 +44,9 @@ if (1 == params.nodes.length) \{ levelName = data.nodes[params.nodes[0]].label; console.log("Clicked on one node, it's this node: " + levelName); - document.location.href = "http://localhost:1313/levels/" + levelName; + resulting_url = document.location.origin + "/levels/" + levelName; + console.log("Resulting URL: " + resulting_url); + document.location.href = resulting_url; } });