implement random url generator
This commit is contained in:
@@ -9,4 +9,7 @@ tower = { workspace = true }
|
||||
reqwest = {workspace = true}
|
||||
url = {workspace = true}
|
||||
rand = {workspace = true}
|
||||
bytes = {workspace = true}
|
||||
thiserror = {workspace = true}
|
||||
compact_str = {workspace = true}
|
||||
bytes = {workspace = true}
|
||||
regex = "1.11"
|
||||
4
ubw-sward/src/http/mod.rs
Normal file
4
ubw-sward/src/http/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod simple;
|
||||
pub mod random;
|
||||
|
||||
pub use simple::{SimpleHttpRequest, SimpleHttpSward};
|
||||
107
ubw-sward/src/http/random.rs
Normal file
107
ubw-sward/src/http/random.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use compact_str::CompactString;
|
||||
use rand::seq::IteratorRandom;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RandomUrlGenerator<Rng: rand::Rng> {
|
||||
random_engine: Rng,
|
||||
random_parts: Box<[RegexPart]>,
|
||||
}
|
||||
|
||||
impl<Rng: rand::Rng> RandomUrlGenerator<Rng> {
|
||||
pub fn new(random_engine: Rng, template: &str) -> Result<Self, RandomUrlGeneratorBuildError> {
|
||||
let re = Regex::new(r"\[([^\]]+)\](?:\{(\d+)\})?")?;
|
||||
let mut parts: Vec<RegexPart> = Vec::new();
|
||||
let mut last_end = 0;
|
||||
|
||||
for caps in re.captures_iter(template) {
|
||||
let m = caps
|
||||
.get(0)
|
||||
.ok_or(RandomUrlGeneratorBuildError::SyntaxError)?;
|
||||
if m.start() > last_end {
|
||||
parts.push(RegexPart::Literal(template[last_end..m.start()].into()));
|
||||
}
|
||||
let expr = caps
|
||||
.get(1)
|
||||
.ok_or(RandomUrlGeneratorBuildError::SyntaxError)?
|
||||
.as_str();
|
||||
let count = caps
|
||||
.get(2)
|
||||
.map(|m| {
|
||||
m.as_str()
|
||||
.parse::<usize>()
|
||||
.map_err(|_| RandomUrlGeneratorBuildError::SyntaxError)
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(1);
|
||||
let mut chars = Vec::new();
|
||||
let mut chars_iter = expr.chars().peekable();
|
||||
while let Some(c) = chars_iter.next() {
|
||||
if let Some(&dash) = chars_iter.peek()
|
||||
&& dash == '-'
|
||||
{
|
||||
chars_iter.next();
|
||||
if let Some(end) = chars_iter.next() {
|
||||
for ch in c..=end {
|
||||
chars.push(ch);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chars.push(c);
|
||||
}
|
||||
let chars = chars.into_boxed_slice();
|
||||
parts.push(RegexPart::RandomChars { chars, count });
|
||||
last_end = m.end();
|
||||
}
|
||||
if last_end < template.len() {
|
||||
parts.push(RegexPart::Literal(template[last_end..].into()));
|
||||
}
|
||||
Ok(Self {
|
||||
random_engine,
|
||||
random_parts: parts.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
fn expected_length(&self) -> usize {
|
||||
self.random_parts.iter().fold(0, |acc, part| match part {
|
||||
RegexPart::Literal(s) => acc + s.len(),
|
||||
RegexPart::RandomChars { count, .. } => acc + *count,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate(&mut self) -> CompactString {
|
||||
self.random_parts.iter().fold(
|
||||
CompactString::with_capacity(self.expected_length()),
|
||||
|mut acc, part| {
|
||||
match &part {
|
||||
RegexPart::Literal(s) => {
|
||||
acc.push_str(s);
|
||||
}
|
||||
RegexPart::RandomChars { chars, count } => {
|
||||
let next = (0..*count)
|
||||
.filter_map(|_| chars.iter().choose(&mut self.random_engine))
|
||||
.collect::<CompactString>();
|
||||
acc.push_str(&next);
|
||||
}
|
||||
};
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum RandomUrlGeneratorBuildError {
|
||||
#[error("Invalid regex: {0}")]
|
||||
InvalidRegex(#[from] regex::Error),
|
||||
|
||||
#[error("Syntax error in template.")]
|
||||
SyntaxError,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum RegexPart {
|
||||
Literal(CompactString),
|
||||
RandomChars { chars: Box<[char]>, count: usize },
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use tower::Service;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone)]
|
||||
/// A simple http sward that sends one request per call.
|
||||
pub struct SimpleHttpSward {
|
||||
client: Client,
|
||||
method: Method,
|
||||
@@ -1 +1,6 @@
|
||||
pub mod http;
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![deny(clippy::panic)]
|
||||
#![deny(clippy::expect_used)]
|
||||
|
||||
pub mod http;
|
||||
pub mod utils;
|
||||
0
ubw-sward/src/utils/mod.rs
Normal file
0
ubw-sward/src/utils/mod.rs
Normal file
Reference in New Issue
Block a user