Skip to content

Commit

Permalink
filters working; verbose and lsusb tree content todo
Browse files Browse the repository at this point in the history
  • Loading branch information
tuna-f1sh committed Nov 14, 2022
1 parent 9f6fd84 commit 37e8fef
Show file tree
Hide file tree
Showing 4 changed files with 622 additions and 66 deletions.
127 changes: 126 additions & 1 deletion Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ name = "cyme"
authors = ["John Whittington <[email protected]>"]
description = "Port of lsusb to macOS using system_profiler output"
license = "GPL-3.0-or-later"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
categories = ["command-line-utilities"]

[dependencies]
clap = { version = "4.0.22", features = ["derive"] }
colored = "2.0.0"
log = "0.4.17"
pad = "0.1.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.87"
simple_logger = "4.0.0"
143 changes: 128 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use clap::Parser;
use std::env;
use colored::*;
use std::io::{Error, ErrorKind};
use simple_logger::SimpleLogger;

mod system_profiler;

Expand All @@ -14,7 +17,7 @@ struct Args {
#[arg(short, long, default_value_t = false)]
no_colour: bool,

/// Classic dump the physical USB device hierarchy as a tree
/// Classic dump the physical USB device hierarchy as a tree - currently styling is the same but content is not
#[arg(short = 't', long, default_value_t = false)]
lsusb_tree: bool,

Expand All @@ -23,38 +26,148 @@ struct Args {
tree: bool,

/// Show only devices with the specified vendor and product ID numbers (in hexadecimal) in format VID:[PID]
#[arg(short, long)]
device: Option<String>,
#[arg(short = 'd', long)]
vidpid: Option<String>,

/// Show only devices with specified device and/or bus numbers (in decimal) in format [[bus]:][devnum]
#[arg(short, long)]
show: Option<String>,

/// Increase verbosity (show descriptors)
#[arg(short, long, default_value_t = false)]
verbose: bool,
/// Increase verbosity (show descriptors) TODO
// #[arg(short, long, default_value_t = false)]
// verbose: bool,

/// Turn debugging information on
#[arg(short = 'D', long, action = clap::ArgAction::Count)]
debug: u8,
}

macro_rules! eprintexit {
($error:expr) => {
// `stringify!` will convert the expression *as it is* into a string.
eprintln!("{}", $error.to_string().bold().red());
std::process::exit(1);
};
}

fn parse_vidpid(s: &str) -> (Option<u16>, Option<u16>) {
if s.contains(":") {
let vid_split: Vec<&str> = s.split(":").collect();
let vid: Option<u16> = vid_split.first()
.filter(|v| v.len() > 0)
.map_or(None, |v| u32::from_str_radix(v.trim().trim_start_matches("0x"), 16).map(|v| Some(v as u16)).unwrap_or(None));
let pid: Option<u16> = vid_split.last()
.filter(|v| v.len() > 0)
.map_or(None, |v| u32::from_str_radix(v.trim().trim_start_matches("0x"), 16).map(|v| Some(v as u16)).unwrap_or(None));

(vid, pid)
} else {
let vid: Option<u16> = u32::from_str_radix(s.trim().trim_start_matches("0x"), 16).map(|v| Some(v as u16)).unwrap_or(None);

(vid, None)
}
}

fn parse_show(s: &str) -> Result<(Option<u8>, Option<u8>), Error> {
if s.contains(":") {
let split: Vec<&str> = s.split(":").collect();
// TODO this unwrap should return as the result but I struggle with all this chaining...
let bus: Option<u8> = split.first()
.filter(|v| v.len() > 0)
.map_or(None, |v| v.parse::<u8>().map(Some).map_err(|e| Error::new(ErrorKind::Other, e)).unwrap_or(None));
let device = split.last()
.filter(|v| v.len() > 0)
.map_or(None, |v| v.parse::<u8>().map(Some).map_err(|e| Error::new(ErrorKind::Other, e)).unwrap_or(None));

Ok((bus, device))
} else {
let device: Option<u8> = s.trim().parse::<u8>().map(Some).map_err(|e| Error::new(ErrorKind::Other, e)).unwrap_or(None);

Ok((None, device))
}

}

fn main() {
let args = Args::parse();
let sp_usb = system_profiler::get_spusb().unwrap();

match args.debug {
0 => (),
1 => SimpleLogger::new()
.with_utc_timestamps()
.with_level(log::Level::Info.to_level_filter())
.init()
.unwrap(),
2 => SimpleLogger::new()
.with_utc_timestamps()
.with_level(log::Level::Debug.to_level_filter())
.init()
.unwrap(),
3 | _ => SimpleLogger::new()
.with_utc_timestamps()
.with_level(log::Level::Trace.to_level_filter())
.init()
.unwrap(),
}

// just set the env for this process
if args.no_colour {
env::set_var("NO_COLOR", "1");
}

if args.lsusb {
if args.lsusb_tree {
print!("{:+}", sp_usb);
} else {
print!("{:}", sp_usb);
let sp_usb = system_profiler::get_spusb().unwrap_or_else(|e| {
eprintexit!(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to parse system_profiler output: {}", e)));
});

log::debug!("{:#?}", sp_usb);

let mut filter = system_profiler::USBFilter {
vid: None,
pid: None,
bus: None,
port: None,
};

if let Some(vidpid) = &args.vidpid {
let (vid, pid) = parse_vidpid(&vidpid.as_str());
filter.vid = vid;
filter.pid = pid;
}

if let Some(show) = &args.show {
let (bus, port) = parse_show(&show.as_str()).unwrap_or_else(|e| {
eprintexit!(Error::new(ErrorKind::Other, format!("Failed to parse show parameter: {}", e)));
});
filter.bus = bus;
filter.port = port;
}

log::info!("{:?}", filter);

if args.vidpid.is_some() || args.show.is_some() {
let mut devs = sp_usb.get_all_devices();
devs = filter.filter_devices_ref(devs);
for d in devs {
if args.lsusb {
println!("{:}", d);
} else {
println!("{:#}", d);
}
}
} else {
if args.tree {
print!("{:+#}", sp_usb);
if args.lsusb {
if args.lsusb_tree {
eprintln!("lsusb tree is styling only; content is not the same!");
print!("{:+}", sp_usb);
} else {
print!("{:}", sp_usb);
}
} else {
print!("{:#}", sp_usb);
if args.tree {
print!("{:+#}", sp_usb);
} else {
print!("{:#}", sp_usb);
}
}
}
}
Loading

0 comments on commit 37e8fef

Please sign in to comment.