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