M Cargo.lock => Cargo.lock +63 -0
@@ 1,6 1,24 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 36,6 54,20 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "clap"
+version = "2.33.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "core-foundation"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 774,6 806,11 @@ dependencies = [
]
[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "syn"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 797,6 834,14 @@ dependencies = [
]
[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "thiserror"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 908,6 953,11 @@ dependencies = [
]
[[package]]
+name = "unicode-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 933,6 983,11 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 1083,6 1138,7 @@ name = "yooper"
version = "0.1.0"
dependencies = [
"bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"mac_address 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ 1108,6 1164,8 @@ dependencies = [
]
[metadata]
+"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
@@ 1115,6 1173,7 @@ dependencies = [
"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
@@ 1198,8 1257,10 @@ dependencies = [
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
"checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
+"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
"checksum thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
@@ 1212,10 1273,12 @@ dependencies = [
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
+"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
"checksum vcpkg 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
+"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
M yooper/Cargo.toml => yooper/Cargo.toml +6 -1
@@ 6,8 6,9 @@ edition = "2018"
license = "BSD-3-Clause"
[features]
-default = ["description"]
+default = ["description", "cli"]
description = ["reqwest", "serde", "serde_with", "serde-xml-rs"]
+cli = ["clap"]
[dependencies]
bytes = "0.5.4"
@@ 46,4 47,8 @@ optional = true
[dependencies.reqwest]
version = "0.10"
+optional = true
+
+[dependencies.clap]
+version = "2.33"
optional = true=
\ No newline at end of file
M yooper/src/description.rs => yooper/src/description.rs +8 -8
@@ 12,9 12,9 @@ use std::str::FromStr;
#[derive(PartialEq, Debug)]
pub struct DeviceType {
/// Will be None for standard devices specified by the UPnP Forum.
- vendor_domain: Option<String>,
- device_type: String,
- version: String,
+ pub vendor_domain: Option<String>,
+ pub device_type: String,
+ pub version: String,
}
impl ToString for DeviceType {
@@ 54,9 54,9 @@ impl FromStr for DeviceType {
#[derive(Debug, PartialEq)]
pub struct ServiceType {
/// Will be None for standard services specified by the UPnP Forum.
- vendor_domain: Option<String>,
- service_type: String,
- version: String,
+ pub vendor_domain: Option<String>,
+ pub service_type: String,
+ pub version: String,
}
impl FromStr for ServiceType {
@@ 246,8 246,8 @@ where
/// Retrieve and parse a device description.
/// See the location field from [discovery::Device](../discovery/struct.Device.html#structfield.location).
-pub async fn describe(location: String) -> Result<Device, Error> {
- let body = reqwest::get(&location).await?.text().await?;
+pub async fn describe(location: &str) -> Result<Device, Error> {
+ let body = reqwest::get(location).await?.text().await?;
let document: Description = serde_xml_rs::from_str(&body)?;
Ok(document.device)
M yooper/src/main.rs => yooper/src/main.rs +91 -13
@@ 1,22 1,53 @@
-use yooper::{discovery::Discovery, Error};
+#![cfg(feature = "cli")]
-// const VERSION: &'static str = env!("CARGO_PKG_VERSION");
-// const OS: &'static str = "linux"; //TODO
+use clap::{
+ app_from_crate, crate_authors, crate_description, crate_name, crate_version, Arg, SubCommand,
+};
+use yooper::Error;
-// M-SEARCH * HTTP/1.1
-// HOST: 239.255.255.250:1900
-// MAN: "ssdp:discover"
-// MX: seconds to delay response
-// ST: search target
-// USER-AGENT: OS/version UPnP/2.0 product/version
-// CPFN.UPNP.ORG: friendly name of the control point
-// CPUUID.UPNP.ORG: uuid of the control point
+fn validate_secs(v: String) -> Result<(), String> {
+ let msg = "Please specify a number between 1 and 5";
+ let v: u8 = v.parse().map_err(|_| msg.to_owned())?;
+
+ match v {
+ 1..=5 => Ok(()),
+ _ => Err(msg.to_owned()),
+ }
+}
#[tokio::main]
async fn main() -> Result<(), Error> {
- let mut discovery = Discovery::new().await?;
+ let args = app_from_crate!()
+ .setting(clap::AppSettings::SubcommandRequiredElseHelp)
+ .subcommands(vec![
+ SubCommand::with_name("discover")
+ .about("discover UPnP devices on network")
+ .arg(Arg::with_name("timeout")
+ .short("t")
+ .long("timeout")
+ .takes_value(true)
+ .default_value("5")
+ .value_name("TIMEOUT")
+ .help("How long to wait for devices on the network to respond. 1..5 seconds per the UPnP spec.")
+ .validator(validate_secs)),
+ #[cfg(feature = "description")]
+ SubCommand::with_name("describe")
+ .about("Describe a UPnP device's capabilities")
+ .arg(Arg::with_name("url").help("The URL to describe").required(true)),
+ ]).get_matches();
+
+ match args.subcommand() {
+ ("discover", Some(sub_m)) => discover(sub_m.value_of("timeout").unwrap().parse()?).await,
+ #[cfg(feature = "description")]
+ ("describe", Some(sub_m)) => describe::run(sub_m.value_of("url").unwrap()).await,
+ _ => unreachable!(),
+ }
+}
- for result in discovery.find(5).await? {
+async fn discover(secs: u8) -> Result<(), Error> {
+ let mut discovery = yooper::discovery::Discovery::new().await?;
+
+ for result in discovery.find(secs).await? {
println!("{} at {}", result.server, result.location);
for service in result.services {
println!("∟ {:?}", service.target)
@@ 24,3 55,50 @@ async fn main() -> Result<(), Error> {
}
Ok(())
}
+
+#[cfg(feature = "description")]
+mod describe {
+ use yooper::description::{describe, Device};
+ use yooper::Error;
+
+ pub async fn run(url: &str) -> Result<(), Error> {
+ describe(url).await.and_then(|d| {
+ print_device(d, 0);
+ Ok(())
+ })
+ }
+
+ fn print_device(device: Device, indent: u8) {
+ let prefix = if indent == 0 {
+ "".into()
+ } else {
+ format!("{}∟ ", "".repeat((indent - 1).into()))
+ };
+ println!(
+ "{}{}{}:{}",
+ prefix,
+ device
+ .device_type
+ .vendor_domain
+ .map_or("".into(), |v| format!("{}: ", v)),
+ device.device_type.device_type,
+ device.device_type.version,
+ );
+ for svc in device.services {
+ println!(
+ "{}{}{}:{} -> {}",
+ prefix,
+ svc.service_type
+ .vendor_domain
+ .map_or("".into(), |v| format!("{}: ", v)),
+ svc.service_type.service_type,
+ svc.service_type.version,
+ svc.control_url,
+ )
+ }
+
+ for dvc in device.devices {
+ print_device(dvc, indent + 1)
+ }
+ }
+}