Skip to content

Commit

Permalink
feat: add handlebars template to generate Java code.
Browse files Browse the repository at this point in the history
Refs: #555
  • Loading branch information
Nanne Baars committed May 17, 2023
1 parent 26bd277 commit 28840b0
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 56 deletions.
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ edition = "2021"
[dependencies]
clap = { version = "4.2.5", features = ["derive"] }
walkdir = "2"
regex = "1.8.1"
handlebars = "3"

110 changes: 70 additions & 40 deletions cli/src/challenge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,73 +8,103 @@ use walkdir::WalkDir;

use crate::{Difficulty, Platform, Technology};

#[derive(Debug)]
pub struct Challenge {
pub number: u8,
pub technology: Technology,
pub difficulty: Difficulty,
pub project_directory: PathBuf,
pub platform: Platform,
}

impl std::fmt::Display for Challenge {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Technology: {}, Difficulty: {}", self.technology, self.difficulty)
impl Challenge {
fn create_java_sources(&self, project_directory: &PathBuf) {
let challenge_source_path =
project_directory
.join("src/main/java/org/owasp/wrongsecrets/challenges");

let (handlebars, data) = self.init_handlebars(project_directory);
let challenge_source_content = handlebars
.render("challenge", &data)
.expect("Unable to render challenge template");
let mut class_file = File::create(
challenge_source_path
.join(self.platform.to_string())
.join(format!("Challenge{}.java", &self.number.to_string()))
)
.expect("Unable to create challenge source file");
class_file
.write(challenge_source_content.as_bytes())
.expect("Unable to write challenge source file");
}
}

pub fn create_challenge(challenge: &Challenge) {
let challenge_number = &challenge.number.to_string();
let challenge_name = String::from("Challenge") + challenge_number + ".java";
let challenge_exists = check_challenge_exists(challenge);
fn init_handlebars(&self, project_directory: &PathBuf) -> (Handlebars, BTreeMap<String, String>) {
const CHALLENGE_TEMPLATE: &str = "src/main/resources/challenge.hbs";
let mut handlebars = Handlebars::new();
handlebars
.register_template_file(
"challenge",
project_directory.join(CHALLENGE_TEMPLATE),
)
.unwrap();
let mut data: BTreeMap<String, String> = BTreeMap::new();
data.insert("challenge_number".to_string(), self.number.to_string());
data.insert("platform".to_string(), self.platform.to_string().to_lowercase());
data.insert("difficulty".to_string(), self.difficulty.to_string().to_uppercase());
data.insert("technology".to_string(), self.technology.to_string().to_uppercase());

if challenge_exists {
panic!("{:?} already exists", challenge_name);
(handlebars, data)
}

println!("Creating challenge {} in {}", challenge_number, challenge.project_directory.display());
create_challenge_class_file(challenge, challenge_number, challenge_name);
create_documentation_files(challenge, challenge_number);
}

fn create_documentation_files(challenge: &Challenge, challenge_number: &String) {
let challenge_documentation_path = challenge.project_directory.join("src/main/resources/explanations/");
create_documentation_file(challenge_documentation_path.join(format!("challenge{}.adoc", challenge_number)));
create_documentation_file(challenge_documentation_path.join(format!("challenge{}_hint.adoc", challenge_number)));
create_documentation_file(challenge_documentation_path.join(format!("challenge{}_explanation.adoc", challenge_number)));
fn create_documentation(&self, project_directory: &PathBuf) {
let challenge_documentation_path =
project_directory
.join("src/main/resources/explanations/");
create_documentation_file(
challenge_documentation_path.join(format!("challenge{}.adoc", self.number.to_string())),
);
create_documentation_file(
challenge_documentation_path.join(format!("challenge{}_hint.adoc", self.number.to_string())),
);
create_documentation_file(
challenge_documentation_path
.join(format!("challenge{}_explanation.adoc", self.number.to_string())),
);
}
}

fn create_documentation_file(filename: PathBuf) {
File::create(filename).expect("Unable to create challenge documentation file");
}

fn create_challenge_class_file(challenge: &Challenge, challenge_number: &String, challenge_name: String) {
const CHALLENGE_TEMPLATE: &str = "src/main/resources/challenge.hbs";
let challenge_source_path = challenge.project_directory.join("src/main/java/org/owasp/wrongsecrets/challenges");
pub fn create_challenge(challenge: &Challenge, project_directory: &PathBuf) {
let challenge_exists = check_challenge_exists(challenge, &project_directory);

let mut handlebars = Handlebars::new();
handlebars.register_template_file("challenge", challenge.project_directory.join(CHALLENGE_TEMPLATE)).unwrap();
let mut data = BTreeMap::new();
data.insert("challenge_number".to_string(), challenge_number);
let challenge_source_content = handlebars.render("challenge", &data).expect("Unable to render challenge template");
let mut class_file = File::create(challenge_source_path.join(challenge.platform.to_string()).join(challenge_name)).expect("Unable to create challenge source file");
class_file.write(challenge_source_content.as_bytes()).expect("Unable to write challenge source file");
if challenge_exists {
panic!("{:?} already exists", &challenge);
}

println!(
"Creating {:?}",
&challenge
);
challenge.create_java_sources(project_directory);
challenge.create_documentation(project_directory);
}


//File API has `create_new` but it is still experimental in the nightly build, let loop and check if it exists for now
fn check_challenge_exists(challenge: &Challenge) -> bool {
let challenges_directory = challenge.project_directory.join("src/main/java/org/owasp/wrongsecrets/challenges");
fn check_challenge_exists(challenge: &Challenge, project_directory: &PathBuf) -> bool {
let challenges_directory =
project_directory
.join("src/main/java/org/owasp/wrongsecrets/challenges");
let challenge_name = String::from("Challenge") + &challenge.number.to_string() + ".java";

let challenge_exists = WalkDir::new(challenges_directory)
.into_iter()
.filter_map(|e| e.ok())
.any(|e| {
match e.file_name().to_str() {
None => { false }
Some(name) => {
name.eq(challenge_name.as_str())
}
}
.any(|e| match e.file_name().to_str() {
None => false,
Some(name) => name.eq(challenge_name.as_str()),
});
challenge_exists
}
2 changes: 1 addition & 1 deletion cli/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub enum Difficulty {
pub enum Platform {
Cloud,
Docker,
Kubernetes
Kubernetes,
}

impl fmt::Display for Difficulty {
Expand Down
32 changes: 20 additions & 12 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::path::PathBuf;

use clap::arg;
use clap::{Parser, Subcommand};
use clap::arg;

use crate::challenge::Challenge;
use crate::enums::{Difficulty, Platform, Technology};

mod enums;
mod challenge;
mod enums;

#[derive(Debug, Parser)]
#[command(name = "cli")]
Expand All @@ -19,13 +19,14 @@ struct Cli {

#[derive(Debug, Subcommand)]
enum Commands {
#[command(arg_required_else_help = true, name = "challenge", about = "Create a new challenge")]
#[command(
arg_required_else_help = true,
name = "challenge",
about = "Create a new challenge"
)]
ChallengeCommand {
//We could infer this from the directory structure but another PR could already have added the challenge with this number
#[arg(
long,
short,
value_name = "NUMBER")]
#[arg(long, short, value_name = "NUMBER")]
number: u8,
#[arg(
long,
Expand Down Expand Up @@ -57,7 +58,7 @@ enum Commands {
platform: Platform,
#[arg(required = true)]
project_directory: PathBuf,
}
},
}

fn main() {
Expand All @@ -68,11 +69,18 @@ fn main() {
difficulty,
technology,
platform,
project_directory
project_directory,
} => {
project_directory.try_exists().expect("Unable to find project directory");
let challenge = Challenge { number, difficulty, technology, platform, project_directory };
challenge::create_challenge(&challenge);
project_directory
.try_exists()
.expect("Unable to find project directory");
let challenge = Challenge {
number,
difficulty,
technology,
platform,
};
challenge::create_challenge(&challenge, &project_directory);
}
}
}
5 changes: 3 additions & 2 deletions src/main/resources/challenge.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.owasp.wrongsecrets.challenges.{{platform}};
import org.owasp.wrongsecrets.RuntimeEnvironment;
import org.owasp.wrongsecrets.ScoreCard;
import org.owasp.wrongsecrets.challenges.Challenge;
import org.owasp.wrongsecrets.challenges.ChallengeTechnology;
import org.owasp.wrongsecrets.challenges.Difficulty;
import org.owasp.wrongsecrets.challenges.Spoiler;
import org.springframework.core.annotation.Order;
Expand Down Expand Up @@ -43,7 +44,7 @@ public class Challenge{{challenge_number}} extends Challenge {
* {@inheritDoc}
*/
public List<RuntimeEnvironment.Environment> supportedRuntimeEnvironments() {
return List.of(DOCKER);
return List.of(RuntimeEnvironment.Environment.{{environment}});
}

/**
Expand All @@ -56,7 +57,7 @@ public class Challenge{{challenge_number}} extends Challenge {

@Override
public String getTech() {
return ChallengeTechnology.Tech{{technology}}.id;
return ChallengeTechnology.Tech.{{technology}}.id;
}

@Override
Expand Down

0 comments on commit 28840b0

Please sign in to comment.