batman
This commit is contained in:
commit
244e8caa9f
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
1412
Cargo.lock
generated
Normal file
1412
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "ripht"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "ripht"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
rustls = []
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.8", features = ["derive"] }
|
||||
reqwest = "0.12.5"
|
||||
json = "0.12.4"
|
6
ripht.json
Normal file
6
ripht.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"catfacts": {
|
||||
"base": "https://cat-fact.herokuapp.com",
|
||||
"tests": "tests/catfacts"
|
||||
}
|
||||
}
|
35
src/bin/ripht.rs
Normal file
35
src/bin/ripht.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use clap::{Parser, ArgAction};
|
||||
use ripht::{Config, Ripht};
|
||||
use std::fs;
|
||||
|
||||
/// RipHT CLI HTTP REST API client
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Config file
|
||||
#[arg(short, long, default_value = "ripht.json")]
|
||||
conf: String,
|
||||
/// Verbosity
|
||||
#[arg(short, long, action = ArgAction::Count)]
|
||||
verbosity: u8,
|
||||
}
|
||||
|
||||
impl Into<Config> for Args {
|
||||
fn into(self) -> Config {
|
||||
Config {
|
||||
verbosity: self.verbosity,
|
||||
parallel: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Warming up...");
|
||||
let args = Args::parse();
|
||||
let json = json::parse(std::str::from_utf8(
|
||||
&fs::read(&args.conf).unwrap()
|
||||
).unwrap()).unwrap();
|
||||
let rip = Ripht::with_json(args.into(), json);
|
||||
println!("Letting it RIP!");
|
||||
rip.work();
|
||||
}
|
61
src/lib.rs
Normal file
61
src/lib.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use std::collections::HashMap;
|
||||
use json::JsonValue;
|
||||
use reqwest::Client;
|
||||
|
||||
mod parse;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub parallel: bool,
|
||||
pub verbosity: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Request {
|
||||
headers: HashMap<String, String>,
|
||||
meth: String,
|
||||
path: String,
|
||||
body: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Target {
|
||||
reqs: Vec<Request>,
|
||||
base: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ripht {
|
||||
targets: Vec<Target>,
|
||||
client: Client,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Ripht {
|
||||
pub fn with_json(config: Config, json: JsonValue) -> Self {
|
||||
let targets = match json {
|
||||
JsonValue::Object(obj) => parse::obj(obj),
|
||||
JsonValue::Array(arr) => parse::arr(arr),
|
||||
_ => panic!("Root level of ripht.json config has to be a JSON object or array!"),
|
||||
};
|
||||
let client = Client::new();
|
||||
Self { targets, client, config }
|
||||
}
|
||||
pub fn work(self) {
|
||||
println!("{self:?}");
|
||||
}
|
||||
}
|
||||
/*
|
||||
pub fn ripit(targets: Vec<Target>) {
|
||||
let mut curl = Easy::new();
|
||||
for target in targets {
|
||||
curl.url(target.base).unwrap();
|
||||
curl.write_function(|data| {
|
||||
println!("{data}");
|
||||
Ok(data.len())
|
||||
}).unwrap();
|
||||
curl.perform().unwrap();
|
||||
}
|
||||
}
|
||||
*/
|
31
src/parse/mod.rs
Normal file
31
src/parse/mod.rs
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
use crate::{Request, Target};
|
||||
|
||||
mod req;
|
||||
|
||||
impl Into<Request> for req::Req<'_> {
|
||||
fn into(self) -> Request {
|
||||
Request {
|
||||
meth: self.meth.to_string(), path: self.path.to_string(), body: self.body.to_string(),
|
||||
headers: self.head.into_iter().map(
|
||||
|(name, val)| (name.to_string(), val.to_string())
|
||||
).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn obj(json: json::object::Object) -> Vec<Target> {
|
||||
let mut targets = vec![];
|
||||
for (name, conf) in json.iter() {
|
||||
targets.push(Target {
|
||||
reqs: req::reqs(conf["tests"].to_string()),
|
||||
base: conf["base"].to_string(),
|
||||
name: name.to_string(),
|
||||
})
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
pub fn arr(targets: json::Array) -> Vec<Target> {
|
||||
panic!("Not implemented!");
|
||||
}
|
100
src/parse/req.rs
Normal file
100
src/parse/req.rs
Normal file
@ -0,0 +1,100 @@
|
||||
|
||||
use std::{
|
||||
fs::{self, ReadDir, DirEntry},
|
||||
collections::HashMap,
|
||||
io::Read, str::Chars,
|
||||
};
|
||||
|
||||
use reqwest::dns::Name;
|
||||
|
||||
use crate::Request;
|
||||
|
||||
pub fn reqs<'ctx>(path: String) -> Vec<Request> {
|
||||
// Find path
|
||||
// -> Open files
|
||||
// -> Ppass content to req::Req::parse().into()
|
||||
// Collect
|
||||
dir(fs::read_dir(path).unwrap()).iter().map(
|
||||
|entry| {
|
||||
let mut contents = String::new();
|
||||
fs::File::open(entry.path()).unwrap().read_to_string(&mut contents).unwrap();
|
||||
Req::parse(&contents).into()
|
||||
}
|
||||
).collect()
|
||||
}
|
||||
|
||||
fn dir(read: ReadDir) -> Vec<DirEntry> {
|
||||
let mut files = vec![];
|
||||
for dir_result in read {
|
||||
let dir_entry = dir_result.unwrap();
|
||||
let dir_type = dir_entry.file_type().unwrap();
|
||||
if dir_type.is_dir() {
|
||||
files.append(&mut dir(fs::read_dir(dir_entry.path().to_owned()).unwrap()));
|
||||
}
|
||||
if dir_type.is_file() {
|
||||
files.push(dir_entry);
|
||||
}
|
||||
}
|
||||
files
|
||||
}
|
||||
|
||||
pub struct Req<'ctx> {
|
||||
pub head: HashMap<&'ctx str, &'ctx str>,
|
||||
pub meth: &'ctx str,
|
||||
pub path: &'ctx str,
|
||||
pub body: &'ctx str,
|
||||
pub raw: &'ctx str,
|
||||
}
|
||||
|
||||
impl<'ctx> Req<'ctx> {
|
||||
pub fn parse(raw: &'ctx str) -> Self {
|
||||
let mut head = HashMap::new();
|
||||
let mut chars = raw.chars();
|
||||
let mut idx = 0usize;
|
||||
|
||||
let skip = |mut idx, chars: &mut Chars| {
|
||||
while chars.next().unwrap().is_whitespace() { idx += 1; }
|
||||
idx
|
||||
};
|
||||
idx = skip(idx, &mut chars);
|
||||
let (meth, mut idx) = parse_word(idx, raw, &mut chars);
|
||||
idx = skip(idx, &mut chars);
|
||||
let (path, mut idx) = parse_word(idx, raw, &mut chars);
|
||||
idx = skip(idx, &mut chars);
|
||||
|
||||
let mut lnc = 0;
|
||||
while lnc < 1 {
|
||||
// Unwrap error here should only mean that the body is empty and there is no newline after the headers
|
||||
let next = chars.next().unwrap_or('\n');
|
||||
if next == '\n' {
|
||||
lnc += 1;
|
||||
}
|
||||
if next.is_alphanumeric() {
|
||||
let (name, value, mut idx) = parse_head(idx, raw, &mut chars);
|
||||
head.insert(name, value);
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
idx += 2;
|
||||
let body = if chars.next().is_none() { "" }
|
||||
else { &raw[idx..] };
|
||||
|
||||
Self { head, meth, path, body, raw }
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_word<'ctx>(start: usize, raw: &'ctx str, chars: &mut Chars) -> (&'ctx str, usize) {
|
||||
let mut end = start;
|
||||
while chars.next().unwrap().is_alphabetic() { end += 1; }
|
||||
end += 1;
|
||||
let word = &raw[start..end];
|
||||
end += 1;
|
||||
(word, end)
|
||||
}
|
||||
|
||||
fn parse_head<'ctx>(start: usize, raw: &'ctx str, chars: &mut Chars) -> (&'ctx str, &'ctx str, usize) {
|
||||
let mut end = start;
|
||||
dbg!("parse headers here");
|
||||
end += 1;
|
||||
("", "", end)
|
||||
}
|
2
tests/catfacts/facts.rht
Normal file
2
tests/catfacts/facts.rht
Normal file
@ -0,0 +1,2 @@
|
||||
GET /facts
|
||||
Accept: application/json
|
Loading…
x
Reference in New Issue
Block a user