use super::{ConsoleLevel, ConsoleProvider, RandomProvider, TimeProvider};
use std::time::{Instant, SystemTime, UNIX_EPOCH};
#[cfg(feature = "regex")]
use super::{CompiledRegex, RegExpProvider, RegexMatch};
#[cfg(feature = "regex")]
use std::rc::Rc;
pub struct StdTimeProvider {
epoch: Instant,
}
impl StdTimeProvider {
pub fn new() -> Self {
Self {
epoch: Instant::now(),
}
}
}
impl Default for StdTimeProvider {
fn default() -> Self {
Self::new()
}
}
impl TimeProvider for StdTimeProvider {
fn now_millis(&self) -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as i64)
.unwrap_or(0)
}
fn elapsed_millis(&self, start: u64) -> u64 {
let now = self.epoch.elapsed().as_millis() as u64;
now.saturating_sub(start)
}
fn start_timer(&self) -> u64 {
self.epoch.elapsed().as_millis() as u64
}
}
pub struct StdRandomProvider {
state: u64,
}
impl StdRandomProvider {
pub fn new() -> Self {
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0x12345678_9abcdef0);
let seed = if seed == 0 { 0x12345678_9abcdef0 } else { seed };
Self { state: seed }
}
#[allow(dead_code)]
pub fn with_seed(seed: u64) -> Self {
let seed = if seed == 0 { 1 } else { seed };
Self { state: seed }
}
}
impl Default for StdRandomProvider {
fn default() -> Self {
Self::new()
}
}
impl RandomProvider for StdRandomProvider {
fn random(&mut self) -> f64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
let mantissa = x >> 11; (mantissa as f64) / ((1u64 << 53) as f64)
}
}
pub struct StdConsoleProvider;
impl StdConsoleProvider {
pub fn new() -> Self {
Self
}
}
impl Default for StdConsoleProvider {
fn default() -> Self {
Self::new()
}
}
impl ConsoleProvider for StdConsoleProvider {
fn write(&self, level: ConsoleLevel, message: &str) {
match level {
ConsoleLevel::Log | ConsoleLevel::Info | ConsoleLevel::Debug => {
println!("{message}");
}
ConsoleLevel::Warn | ConsoleLevel::Error => {
eprintln!("{message}");
}
}
}
fn clear(&self) {
println!("\n--- Console cleared ---\n");
}
}
#[cfg(feature = "regex")]
mod regex_impl {
use super::*;
#[derive(Debug, Clone, Copy, Default)]
pub struct FancyRegexProvider;
impl FancyRegexProvider {
pub fn new() -> Self {
Self
}
}
#[derive(Debug)]
pub struct FancyCompiledRegex {
regex: fancy_regex::Regex,
#[allow(dead_code)]
flags: String,
}
impl CompiledRegex for FancyCompiledRegex {
fn is_match(&self, input: &str) -> Result<bool, String> {
self.regex.is_match(input).map_err(|e| e.to_string())
}
fn find(&self, input: &str, start_pos: usize) -> Result<Option<RegexMatch>, String> {
let slice = input.get(start_pos..).unwrap_or("");
match self.regex.captures(slice) {
Ok(Some(caps)) => {
let full_match = caps.get(0).ok_or("No match found")?;
let captures: Vec<Option<(usize, usize)>> = caps
.iter()
.map(|m| m.map(|c| (start_pos + c.start(), start_pos + c.end())))
.collect();
Ok(Some(RegexMatch {
start: start_pos + full_match.start(),
end: start_pos + full_match.end(),
captures,
}))
}
Ok(None) => Ok(None),
Err(e) => Err(e.to_string()),
}
}
fn find_iter(&self, input: &str) -> Result<Vec<RegexMatch>, String> {
let mut results = Vec::new();
for caps_result in self.regex.captures_iter(input) {
match caps_result {
Ok(caps) => {
let full_match = caps.get(0).ok_or("No match found")?;
let captures: Vec<Option<(usize, usize)>> = caps
.iter()
.map(|m| m.map(|c| (c.start(), c.end())))
.collect();
results.push(RegexMatch {
start: full_match.start(),
end: full_match.end(),
captures,
});
}
Err(e) => return Err(e.to_string()),
}
}
Ok(results)
}
fn split(&self, input: &str) -> Result<Vec<String>, String> {
let parts: Result<Vec<_>, _> = self
.regex
.split(input)
.map(|r| r.map(|s| s.to_string()))
.collect();
parts.map_err(|e| e.to_string())
}
fn replace(&self, input: &str, replacement: &str) -> Result<String, String> {
match self.regex.captures(input) {
Ok(Some(caps)) => {
let full_match = caps.get(0).ok_or("No match found")?;
let replacement_str = expand_replacement(replacement, &caps);
let mut result = String::with_capacity(input.len());
result.push_str(input.get(..full_match.start()).unwrap_or(""));
result.push_str(&replacement_str);
result.push_str(input.get(full_match.end()..).unwrap_or(""));
Ok(result)
}
Ok(None) => Ok(input.to_string()),
Err(e) => Err(e.to_string()),
}
}
fn replace_all(&self, input: &str, replacement: &str) -> Result<String, String> {
let mut result = String::with_capacity(input.len());
let mut last_end = 0;
for caps_result in self.regex.captures_iter(input) {
match caps_result {
Ok(caps) => {
let full_match = caps.get(0).ok_or("No match found")?;
let replacement_str = expand_replacement(replacement, &caps);
result.push_str(input.get(last_end..full_match.start()).unwrap_or(""));
result.push_str(&replacement_str);
last_end = full_match.end();
}
Err(e) => return Err(e.to_string()),
}
}
result.push_str(input.get(last_end..).unwrap_or(""));
Ok(result)
}
}
fn expand_replacement(replacement: &str, caps: &fancy_regex::Captures) -> String {
let mut result = String::with_capacity(replacement.len());
let chars: Vec<char> = replacement.chars().collect();
let mut i = 0;
while i < chars.len() {
let Some(&c) = chars.get(i) else { break };
let Some(&next) = chars.get(i + 1) else {
result.push(c);
i += 1;
continue;
};
if c == '$' {
if next == '$' {
result.push('$');
i += 2;
} else if next == '&' {
if let Some(m) = caps.get(0) {
result.push_str(m.as_str());
}
i += 2;
} else if next.is_ascii_digit() {
let mut num_str = String::new();
let mut j = i + 1;
while let Some(&ch) = chars.get(j) {
if !ch.is_ascii_digit() || num_str.len() >= 2 {
break;
}
num_str.push(ch);
j += 1;
}
if let Ok(group_num) = num_str.parse::<usize>()
&& let Some(m) = caps.get(group_num)
{
result.push_str(m.as_str());
}
i = j;
} else {
result.push('$');
i += 1;
}
} else {
result.push(c);
i += 1;
}
}
result
}
impl RegExpProvider for FancyRegexProvider {
fn compile(&self, pattern: &str, flags: &str) -> Result<Rc<dyn CompiledRegex>, String> {
let mut regex_pattern = js_regex_to_rust(pattern);
let mut prefix = String::new();
if flags.contains('i') {
prefix.push('i');
}
if flags.contains('m') {
prefix.push('m');
}
if flags.contains('s') {
prefix.push('s');
}
if !prefix.is_empty() {
regex_pattern = format!("(?{}){}", prefix, regex_pattern);
}
let regex = fancy_regex::Regex::new(®ex_pattern)
.map_err(|e| format!("Invalid regular expression: {}", e))?;
Ok(Rc::new(FancyCompiledRegex {
regex,
flags: flags.to_string(),
}))
}
}
fn js_regex_to_rust(pattern: &str) -> String {
let mut result = String::with_capacity(pattern.len() + 16);
let chars: Vec<char> = pattern.chars().collect();
let len = chars.len();
let mut i = 0;
let mut in_char_class = false;
let mut char_class_start = false;
while i < len {
let Some(c) = chars.get(i).copied() else {
break;
};
if c == '\\'
&& let Some(next) = chars.get(i + 1).copied()
{
result.push(c);
result.push(next);
i += 2;
char_class_start = false;
continue;
}
if !in_char_class {
if c == '[' {
in_char_class = true;
char_class_start = true;
result.push(c);
} else {
result.push(c);
}
} else {
if char_class_start {
if c == '^' {
result.push(c);
} else if c == ']' {
result.push(c);
char_class_start = false;
} else if c == '[' {
result.push('\\');
result.push('[');
char_class_start = false;
} else {
result.push(c);
char_class_start = false;
}
} else if c == ']' {
in_char_class = false;
result.push(c);
} else if c == '[' {
result.push('\\');
result.push('[');
} else {
result.push(c);
}
}
i += 1;
}
result
}
}
#[cfg(feature = "regex")]
pub use regex_impl::FancyRegexProvider;