feat(tools): cli to help generate files for new problems, and problem 1 rewritten in rust
This commit is contained in:
parent
a0a6f8c8f7
commit
62becdbc65
6 changed files with 2066 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/ignore
|
||||
|
|
1903
Cargo.lock
generated
Normal file
1903
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
Cargo.toml
11
Cargo.toml
|
@ -1,8 +1,17 @@
|
|||
[package]
|
||||
name = "euler"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
default-run = "euler"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0.4", features = ["derive"] }
|
||||
owo-colors = "3.5.0"
|
||||
scraper = "0.14.0"
|
||||
requestty = "0.4.1"
|
||||
reqwest = "0.11"
|
||||
tokio = { version = "1.23", features = ["full"] }
|
||||
regex = "1"
|
||||
html-escape = "0.2.13"
|
||||
|
|
1
readme.md
Normal file
1
readme.md
Normal file
|
@ -0,0 +1 @@
|
|||
# readme coming soon
|
29
src/bin/1.rs
Normal file
29
src/bin/1.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Problem 1 - Multiples of 3 or 5
|
||||
|
||||
// If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
|
||||
// Find the sum of all the multiples of 3 or 5 below 1000.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn multiples_of(multipliers: &Vec<usize>, upper_bound: usize) -> HashSet<usize> {
|
||||
let mut results: HashSet<usize> = HashSet::new();
|
||||
|
||||
// Find all of the numbers between 1 and the upper bound that are multiples of one of the numbers
|
||||
// in the multipliers vec
|
||||
for i in 1..upper_bound {
|
||||
for number in multipliers {
|
||||
if i % number == 0 {
|
||||
results.insert(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let multiples = multiples_of(&vec![3, 5], 1000);
|
||||
let sum: usize = multiples.iter().sum();
|
||||
|
||||
println!("The sum of the multiples of 3 and 5 up until 1000 is {sum}");
|
||||
}
|
124
src/main.rs
124
src/main.rs
|
@ -1,3 +1,123 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use clap::{Parser, Subcommand};
|
||||
use owo_colors::OwoColorize;
|
||||
use std::{fs, path::Path, io::Write};
|
||||
use scraper::{Html, Selector};
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(about, author, version)]
|
||||
struct Value {
|
||||
#[clap(subcommand)]
|
||||
command: Commands
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Handles the initialisation of a new Project Euler Problem
|
||||
New,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn new() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let code_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("bin");
|
||||
|
||||
let problem_number = requestty::prompt_one(
|
||||
requestty::Question::int("problemNumber")
|
||||
.message("Which problem would you like to solve?")
|
||||
.validate(|n, _| {
|
||||
// All numbers must be positive
|
||||
let mut pass = n > 0;
|
||||
|
||||
// Ensure that the problem has not already got a file associated with it
|
||||
let files = fs::read_dir(&code_path).unwrap();
|
||||
|
||||
for file in files {
|
||||
let file_number = file
|
||||
.unwrap()
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.split('.')
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.parse::<i64>()
|
||||
.unwrap();
|
||||
|
||||
if n == file_number && pass {
|
||||
pass = false;
|
||||
}
|
||||
}
|
||||
|
||||
if pass {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Please ensure that your input is valid!".to_owned())
|
||||
}
|
||||
})
|
||||
.build()
|
||||
).unwrap().as_int().unwrap();
|
||||
|
||||
// todo: thoughts documents (?)
|
||||
|
||||
// Fetch the problem information
|
||||
let body = reqwest::get(format!("https://projecteuler.net/problem={problem_number}")).await?.text().await?;
|
||||
|
||||
let document = Html::parse_document(Box::leak(body.into_boxed_str()));
|
||||
let title_selector = Selector::parse("h2")?;
|
||||
let content_selector = Selector::parse(".problem_content")?;
|
||||
let re = Regex::new(r"<[^>]*>").unwrap();
|
||||
|
||||
let mut title = document
|
||||
.select(&title_selector)
|
||||
.next()
|
||||
.unwrap()
|
||||
.text()
|
||||
.collect::<Vec<&str>>()
|
||||
.join("");
|
||||
|
||||
let mut problem = re.replace_all(
|
||||
Box::leak(
|
||||
document
|
||||
.select(&content_selector)
|
||||
.next()
|
||||
.unwrap()
|
||||
.inner_html()
|
||||
.into_boxed_str()
|
||||
),
|
||||
" "
|
||||
).to_string();
|
||||
|
||||
let file_body = format!(
|
||||
"// Problem {} - {}
|
||||
|
||||
//{}
|
||||
|
||||
fn main() {{
|
||||
println!(\"Hello World!\");
|
||||
}}",
|
||||
problem_number,
|
||||
html_escape::decode_html_entities(&mut title).to_string(),
|
||||
html_escape::decode_html_entities(&mut problem).split("\n").filter(|s| s != &"").collect::<Vec<&str>>().join("\n//")
|
||||
);
|
||||
|
||||
// Create the file
|
||||
let mut file = fs::File::create(code_path.join(format!("{problem_number}.rs"))).unwrap();
|
||||
file.write(file_body.as_bytes()).unwrap();
|
||||
drop(file);
|
||||
|
||||
// todo: update readme
|
||||
|
||||
// Announce completion!
|
||||
println!("{}", "File successfully created! Good luck (:".green());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// todo: runner
|
||||
|
||||
fn main() {
|
||||
let value = Value::parse();
|
||||
|
||||
match value.command {
|
||||
Commands::New => new().unwrap(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue