Skip to content

Commit

Permalink
Add a fake pinger implementation, switch to lazy-regex, refactor ping…
Browse files Browse the repository at this point in the history
…er/parser traits
  • Loading branch information
orf committed Nov 7, 2023
1 parent 5adb93d commit 071ab01
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 102 deletions.
74 changes: 72 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pinger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ repository = "https://github.com/orf/pinger/"

[dependencies]
anyhow = "1.0.75"
regex = "1.10.2"
lazy_static = "1.4.0"
thiserror = "1.0.50"
rand = "0.8.5"
lazy-regex = "3.0.2"

[target.'cfg(windows)'.dependencies]
winping = "0.10.1"
Expand Down
18 changes: 8 additions & 10 deletions pinger/src/bsd.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
use crate::{Parser, PingResult, Pinger};
use regex::Regex;
use lazy_regex::*;
use std::time::Duration;

lazy_static! {
static ref RE: Regex = Regex::new(r"time=(?:(?P<ms>[0-9]+).(?P<ns>[0-9]+)\s+ms)").unwrap();
}
pub static RE: Lazy<Regex> = lazy_regex!(r"time=(?:(?P<ms>[0-9]+).(?P<ns>[0-9]+)\s+ms)");

#[derive(Default)]
pub struct BSDPinger {
interval: Duration,
interface: Option<String>,
}

impl Pinger for BSDPinger {
fn set_interval(&mut self, interval: Duration) {
self.interval = interval;
}
type Parser = BSDParser;

fn set_interface(&mut self, interface: Option<String>) {
self.interface = interface;
fn new(interval: Duration, interface: Option<String>) -> Self {
Self {
interface,
interval,
}
}

fn ping_args(&self, target: String) -> (&str, Vec<String>) {
Expand Down
54 changes: 54 additions & 0 deletions pinger/src/fake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::{Parser, PingResult, Pinger};
use rand::prelude::*;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::thread;
use std::time::Duration;

pub struct FakePinger {
interval: Duration,
}

impl Pinger for FakePinger {
type Parser = FakeParser;

fn new(interval: Duration, _interface: Option<String>) -> Self {
Self { interval }
}

fn start(&self, _target: String) -> anyhow::Result<Receiver<PingResult>> {
let (tx, rx) = mpsc::channel();
let sleep_time = self.interval;

thread::spawn(move || {
let mut random = rand::thread_rng();
loop {
let fake_seconds = random.gen_range(50..150);
let ping_result = PingResult::Pong(
Duration::from_millis(fake_seconds),
format!("Fake ping line: {fake_seconds} ms"),
);
if tx.send(ping_result).is_err() {
break;
}

std::thread::sleep(sleep_time);
}
});

Ok(rx)
}

fn ping_args(&self, _target: String) -> (&str, Vec<String>) {
unimplemented!("ping_args not implemented for FakePinger")
}
}

#[derive(Default)]
pub struct FakeParser {}

impl Parser for FakeParser {
fn parse(&self, _line: String) -> Option<PingResult> {
unimplemented!("parse for FakeParser not implemented")
}
}
69 changes: 26 additions & 43 deletions pinger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::linux::{detect_linux_ping, LinuxPingType};
/// }
/// ```
use anyhow::{Context, Result};
use regex::Regex;
use lazy_regex::Regex;
use std::fmt::Formatter;
use std::io::{BufRead, BufReader};
use std::process::{Child, Command, ExitStatus, Stdio};
Expand All @@ -26,16 +26,14 @@ use std::time::Duration;
use std::{fmt, thread};
use thiserror::Error;

#[macro_use]
extern crate lazy_static;

pub mod linux;
// pub mod alpine'
pub mod macos;
#[cfg(windows)]
pub mod windows;

mod bsd;
mod fake;
#[cfg(test)]
mod test;

Expand All @@ -52,18 +50,19 @@ pub fn run_ping(cmd: &str, args: Vec<String>) -> Result<Child> {
.with_context(|| format!("Failed to run ping with args {:?}", &args))
}

pub trait Pinger: Default {
fn start<P>(&self, target: String) -> Result<mpsc::Receiver<PingResult>>
where
P: Parser,
{
pub trait Pinger {
type Parser: Parser;

fn new(interval: Duration, interface: Option<String>) -> Self;

fn start(&self, target: String) -> Result<mpsc::Receiver<PingResult>> {
let (tx, rx) = mpsc::channel();
let (cmd, args) = self.ping_args(target);
let mut child = run_ping(cmd, args)?;
let stdout = child.stdout.take().context("child did not have a stdout")?;

thread::spawn(move || {
let parser = P::default();
let parser = Self::Parser::default();
let reader = BufReader::new(stdout).lines();
for line in reader {
match line {
Expand All @@ -85,25 +84,11 @@ pub trait Pinger: Default {
Ok(rx)
}

fn set_interval(&mut self, interval: Duration);

fn set_interface(&mut self, interface: Option<String>);

fn ping_args(&self, target: String) -> (&str, Vec<String>) {
("ping", vec![target])
}
}

// Default empty implementation of a pinger.
#[derive(Default)]
pub struct SimplePinger {}

impl Pinger for SimplePinger {
fn set_interval(&mut self, _interval: Duration) {}

fn set_interface(&mut self, _interface: Option<String>) {}
}

pub trait Parser: Default {
fn parse(&self, line: String) -> Option<PingResult>;

Expand Down Expand Up @@ -181,11 +166,17 @@ pub fn ping_with_interval(
interval: Duration,
interface: Option<String>,
) -> Result<mpsc::Receiver<PingResult>> {
if std::env::var("PINGER_FAKE_PING")
.map(|e| e == "1")
.unwrap_or(false)
{
let fake = fake::FakePinger::new(interval, interface);
return fake.start(addr);
}

#[cfg(windows)]
{
let mut p = windows::WindowsPinger::default();
p.set_interval(interval);
p.set_interface(interface);
let p = windows::WindowsPinger::new(interval, interface);
return p.start::<windows::WindowsParser>(addr);

Check failure on line 180 in pinger/src/lib.rs

View workflow job for this annotation

GitHub Actions / Rust project (windows-latest)

method takes 0 generic arguments but 1 generic argument was supplied
}
#[cfg(unix)]
Expand All @@ -195,28 +186,20 @@ pub fn ping_with_interval(
|| cfg!(target_os = "openbsd")
|| cfg!(target_os = "netbsd")
{
let mut p = bsd::BSDPinger::default();
p.set_interval(interval);
p.set_interface(interface);
p.start::<bsd::BSDParser>(addr)
let p = bsd::BSDPinger::new(interval, interface);
p.start(addr)
} else if cfg!(target_os = "macos") {
let mut p = macos::MacOSPinger::default();
p.set_interval(interval);
p.set_interface(interface);
p.start::<macos::MacOSParser>(addr)
let p = macos::MacOSPinger::new(interval, interface);
p.start(addr)
} else {
match detect_linux_ping() {
Ok(LinuxPingType::IPTools) => {
let mut p = linux::LinuxPinger::default();
p.set_interval(interval);
p.set_interface(interface);
p.start::<linux::LinuxParser>(addr)
let p = linux::LinuxPinger::new(interval, interface);
p.start(addr)
}
Ok(LinuxPingType::BusyBox) => {
let mut p = linux::AlpinePinger::default();
p.set_interval(interval);
p.set_interface(interface);
p.start::<linux::LinuxParser>(addr)
let p = linux::AlpinePinger::new(interval, interface);
p.start(addr)
}
Err(e) => Err(PingError::UnsupportedPing(e))?,
}
Expand Down
Loading

0 comments on commit 071ab01

Please sign in to comment.