Skip to content

Commit 5698884

Browse files
committedOct 26, 2019
rustfmt
1 parent 60387bc commit 5698884

25 files changed

+1067
-587
lines changed
 

‎rustfmt.toml

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
max_width = 88
2+
hard_tabs = false
3+
tab_spaces = 2
4+
newline_style = "Auto"
5+
use_small_heuristics = "Default"
6+
indent_style = "Block"
7+
wrap_comments = false
8+
format_code_in_doc_comments = false
9+
comment_width = 80
10+
normalize_comments = false
11+
normalize_doc_attributes = false
12+
license_template_path = ""
13+
format_strings = false
14+
format_macro_matchers = false
15+
format_macro_bodies = true
16+
empty_item_single_line = true
17+
struct_lit_single_line = true
18+
fn_single_line = true
19+
where_single_line = false
20+
imports_indent = "Block"
21+
imports_layout = "Mixed"
22+
merge_imports = false
23+
reorder_imports = true
24+
reorder_modules = true
25+
reorder_impl_items = false
26+
type_punctuation_density = "Wide"
27+
space_before_colon = false
28+
space_after_colon = true
29+
spaces_around_ranges = false
30+
binop_separator = "Front"
31+
remove_nested_parens = true
32+
combine_control_expr = true
33+
overflow_delimited_expr = false
34+
struct_field_align_threshold = 0
35+
enum_discrim_align_threshold = 0
36+
match_arm_blocks = true
37+
force_multiline_blocks = false
38+
fn_args_layout = "Tall"
39+
brace_style = "SameLineWhere"
40+
control_brace_style = "AlwaysSameLine"
41+
trailing_semicolon = true
42+
trailing_comma = "Never"
43+
match_block_trailing_comma = false
44+
blank_lines_upper_bound = 1
45+
blank_lines_lower_bound = 0
46+
edition = "2018"
47+
version = "One"
48+
inline_attribute_width = 0
49+
merge_derives = true
50+
use_try_shorthand = false
51+
use_field_init_shorthand = false
52+
force_explicit_abi = true
53+
condense_wildcard_suffixes = false
54+
color = "Auto"
55+
required_version = "1.4.9"
56+
unstable_features = false
57+
disable_all_formatting = false
58+
skip_children = false
59+
hide_parse_errors = false
60+
error_on_line_overflow = false
61+
error_on_unformatted = false
62+
report_todo = "Never"
63+
report_fixme = "Never"
64+
ignore = []
65+
emit_mode = "Files"
66+
make_backup = false

‎src/lib.rs

+380-294
Large diffs are not rendered by default.

‎src/main.rs

+129-54
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ extern crate clap;
44
#[macro_use]
55
extern crate error_chain;
66

7-
use clap::{Arg, ArgMatches, App, AppSettings};
8-
use ronor::{Sonos, Favorite, Group, Household, Player, Playlist, PlayModes};
7+
use clap::{App, AppSettings, Arg, ArgMatches};
8+
use ronor::{Favorite, Group, Household, PlayModes, Player, Playlist, Sonos};
99
use std::convert::TryFrom;
1010
use xdg::BaseDirectories;
1111

@@ -58,10 +58,28 @@ macro_rules! subcmds {
5858
}
5959
}
6060

61-
subcmds!(init, login,
62-
get_favorites, get_playlist, get_playlists, get_volume, inventory, load_audio_clip,
63-
load_favorite, load_home_theater_playback, load_line_in, load_playlist,
64-
modify_group, now_playing, pause, play, seek, set_mute, set_volume, skip, speak,
61+
subcmds!(
62+
init,
63+
login,
64+
get_favorites,
65+
get_playlist,
66+
get_playlists,
67+
get_volume,
68+
inventory,
69+
load_audio_clip,
70+
load_favorite,
71+
load_home_theater_playback,
72+
load_line_in,
73+
load_playlist,
74+
modify_group,
75+
now_playing,
76+
pause,
77+
play,
78+
seek,
79+
set_mute,
80+
set_volume,
81+
skip,
82+
speak,
6583
toggle_play_pause
6684
);
6785

@@ -72,22 +90,39 @@ fn build() -> App<'static, 'static> {
7290
.about("Sonos smart speaker controller")
7391
.setting(AppSettings::ArgRequiredElseHelp)
7492
.subcommands(build_subcmds())
75-
.subcommand(App::new("get-groups").setting(AppSettings::Hidden)
76-
.about("Get list of groups"))
77-
.subcommand(App::new("get-players").setting(AppSettings::Hidden)
78-
.about("Get list of players"))
79-
.subcommand(App::new("get-playback-status").setting(AppSettings::Hidden)
80-
.about("Get playback status (DEBUG)")
81-
.arg(Arg::with_name("GROUP")))
82-
.subcommand(App::new("get-metadata-status").setting(AppSettings::Hidden)
83-
.about("Get playback status (DEBUG)")
84-
.arg(Arg::with_name("GROUP")))
85-
.subcommand(App::new("completions").setting(AppSettings::Hidden)
86-
.about("Generates completion scripts for your shell")
87-
.arg(Arg::with_name("SHELL")
88-
.required(true)
89-
.possible_values(&["bash", "fish", "zsh"])
90-
.help("The shell to generate the script for")))
93+
.subcommand(
94+
App::new("get-groups")
95+
.setting(AppSettings::Hidden)
96+
.about("Get list of groups")
97+
)
98+
.subcommand(
99+
App::new("get-players")
100+
.setting(AppSettings::Hidden)
101+
.about("Get list of players")
102+
)
103+
.subcommand(
104+
App::new("get-playback-status")
105+
.setting(AppSettings::Hidden)
106+
.about("Get playback status (DEBUG)")
107+
.arg(Arg::with_name("GROUP"))
108+
)
109+
.subcommand(
110+
App::new("get-metadata-status")
111+
.setting(AppSettings::Hidden)
112+
.about("Get playback status (DEBUG)")
113+
.arg(Arg::with_name("GROUP"))
114+
)
115+
.subcommand(
116+
App::new("completions")
117+
.setting(AppSettings::Hidden)
118+
.about("Generates completion scripts for your shell")
119+
.arg(
120+
Arg::with_name("SHELL")
121+
.required(true)
122+
.possible_values(&["bash", "fish", "zsh"])
123+
.help("The shell to generate the script for")
124+
)
125+
)
91126
}
92127

93128
quick_main!(run);
@@ -101,17 +136,15 @@ fn run() -> Result<()> {
101136
let shell = matches.value_of("SHELL").unwrap();
102137
build().gen_completions_to(
103138
"ronor",
104-
shell.parse::<>().unwrap(),
139+
shell.parse().unwrap(),
105140
&mut std::io::stdout()
106141
);
107142
Ok(())
108-
},
109-
("get-playback-status", Some(matches)) =>
110-
get_playback_status(&mut sonos, matches),
111-
("get-groups", Some(matches)) => get_groups(&mut sonos, matches),
112-
("get-metadata-status", Some(matches)) =>
113-
get_metadata_status(&mut sonos, matches),
114-
("get-players", Some(matches)) => get_players(&mut sonos, matches),
143+
}
144+
("get-playback-status", Some(matches)) => get_playback_status(&mut sonos, matches),
145+
("get-groups", Some(matches)) => get_groups(&mut sonos, matches),
146+
("get-metadata-status", Some(matches)) => get_metadata_status(&mut sonos, matches),
147+
("get-players", Some(matches)) => get_players(&mut sonos, matches),
115148
(cmd, Some(matches)) => sonos.run_subcmd(cmd, matches),
116149
_ => unreachable!()
117150
}
@@ -120,11 +153,17 @@ fn run() -> Result<()> {
120153
fn get_playback_status(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
121154
let mut found = false;
122155
for household in sonos.get_households()?.iter() {
123-
for group in sonos.get_groups(&household)?.groups.iter().filter(|group|
124-
matches.value_of("GROUP").map_or(true, |name| name == group.name)
125-
) {
156+
for group in sonos.get_groups(&household)?.groups.iter().filter(|group| {
157+
matches
158+
.value_of("GROUP")
159+
.map_or(true, |name| name == group.name)
160+
}) {
126161
found = true;
127-
println!("{:?} => {:#?}", group.name, sonos.get_playback_status(&group)?);
162+
println!(
163+
"{:?} => {:#?}",
164+
group.name,
165+
sonos.get_playback_status(&group)?
166+
);
128167
}
129168
}
130169
if !found {
@@ -138,11 +177,17 @@ fn get_playback_status(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
138177
fn get_metadata_status(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
139178
let mut found = false;
140179
for household in sonos.get_households()?.iter() {
141-
for group in sonos.get_groups(&household)?.groups.iter().filter(|group|
142-
matches.value_of("GROUP").map_or(true, |name| name == group.name)
143-
) {
180+
for group in sonos.get_groups(&household)?.groups.iter().filter(|group| {
181+
matches
182+
.value_of("GROUP")
183+
.map_or(true, |name| name == group.name)
184+
}) {
144185
found = true;
145-
println!("{:?} => {:#?}", group.name, sonos.get_metadata_status(&group)?);
186+
println!(
187+
"{:?} => {:#?}",
188+
group.name,
189+
sonos.get_metadata_status(&group)?
190+
);
146191
}
147192
}
148193
if !found {
@@ -173,26 +218,41 @@ fn get_players(sonos: &mut Sonos, _matches: &ArgMatches) -> Result<()> {
173218

174219
fn household_arg() -> Arg<'static, 'static> {
175220
Arg::with_name("HOUSEHOLD")
176-
.long("household").takes_value(true).value_name("INDEX")
221+
.long("household")
222+
.takes_value(true)
223+
.value_name("INDEX")
177224
.help("Optional 0-based household index")
178225
}
179226

180227
fn play_modes_args() -> Vec<Arg<'static, 'static>> {
181-
vec![Arg::with_name("REPEAT").short("r").long("repeat"),
182-
Arg::with_name("REPEAT_ONE").short("o").long("repeat-one"),
183-
Arg::with_name("CROSSFADE").short("c").long("crossfade")
184-
.help("Do crossfade between tracks"),
185-
Arg::with_name("SHUFFLE").short("s").long("shuffle")
186-
.help("Shuffle the tracks")
228+
vec![
229+
Arg::with_name("REPEAT").short("r").long("repeat"),
230+
Arg::with_name("REPEAT_ONE").short("o").long("repeat-one"),
231+
Arg::with_name("CROSSFADE")
232+
.short("c")
233+
.long("crossfade")
234+
.help("Do crossfade between tracks"),
235+
Arg::with_name("SHUFFLE")
236+
.short("s")
237+
.long("shuffle")
238+
.help("Shuffle the tracks"),
187239
]
188240
}
189241

190242
trait ArgMatchesExt {
191243
fn household(self: &Self, sonos: &mut Sonos) -> Result<Household>;
192-
fn favorite(self: &Self, sonos: &mut Sonos, household: &Household) -> Result<Favorite>;
244+
fn favorite(
245+
self: &Self,
246+
sonos: &mut Sonos,
247+
household: &Household
248+
) -> Result<Favorite>;
193249
fn group<'a>(self: &Self, groups: &'a [Group]) -> Result<&'a Group>;
194250
fn player<'a>(self: &Self, players: &'a [Player]) -> Result<&'a Player>;
195-
fn playlist(self: &Self, sonos: &mut Sonos, household: &Household) -> Result<Playlist>;
251+
fn playlist(
252+
self: &Self,
253+
sonos: &mut Sonos,
254+
household: &Household
255+
) -> Result<Playlist>;
196256
fn play_modes(self: &Self) -> Option<PlayModes>;
197257
}
198258

@@ -205,31 +265,41 @@ impl ArgMatchesExt for ArgMatches<'_> {
205265
_ => match self.value_of("HOUSEHOLD") {
206266
None => Err("Multiple households found".into()),
207267
Some(index) => {
208-
let index = index.parse::<usize>().chain_err(|| "Invalid household index")?;
268+
let index = index
269+
.parse::<usize>()
270+
.chain_err(|| "Invalid household index")?;
209271
for (n, household) in households.into_iter().enumerate() {
210272
if n == index {
211-
return Ok(household)
273+
return Ok(household);
212274
}
213275
}
214276
Err("Household out of range".into())
215277
}
216278
}
217279
}
218280
}
219-
fn favorite(self: &Self, sonos: &mut Sonos, household: &Household) -> Result<Favorite> {
281+
fn favorite(
282+
self: &Self,
283+
sonos: &mut Sonos,
284+
household: &Household
285+
) -> Result<Favorite> {
220286
let favorite_name = self.value_of("FAVORITE").unwrap();
221287
for favorite in sonos.get_favorites(household)?.items.into_iter() {
222288
if favorite.name == favorite_name {
223-
return Ok(favorite)
289+
return Ok(favorite);
224290
}
225291
}
226292
Err("Playlist not found".into())
227293
}
228-
fn playlist(self: &Self, sonos: &mut Sonos, household: &Household) -> Result<Playlist> {
294+
fn playlist(
295+
self: &Self,
296+
sonos: &mut Sonos,
297+
household: &Household
298+
) -> Result<Playlist> {
229299
let playlist_name = self.value_of("PLAYLIST").unwrap();
230300
for playlist in sonos.get_playlists(household)?.playlists.into_iter() {
231301
if playlist.name == playlist_name {
232-
return Ok(playlist)
302+
return Ok(playlist);
233303
}
234304
}
235305
Err("Playlist not found".into())
@@ -258,7 +328,12 @@ impl ArgMatchesExt for ArgMatches<'_> {
258328
let crossfade = self.is_present("CROSSFADE");
259329
let shuffle = self.is_present("SHUFFLE");
260330
if repeat || repeat_one || crossfade || shuffle {
261-
Some(PlayModes { repeat, repeat_one, crossfade, shuffle })
331+
Some(PlayModes {
332+
repeat,
333+
repeat_one,
334+
crossfade,
335+
shuffle
336+
})
262337
} else {
263338
None
264339
}

‎src/subcmds/get_favorites.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use clap::{ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "get-favorites";
66

‎src/subcmds/get_playlist.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "get-playlist";
66

@@ -16,10 +16,8 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
1616
let playlist = matches.playlist(sonos, &household)?;
1717
for track in sonos.get_playlist(&household, &playlist)?.tracks.iter() {
1818
match &track.album {
19-
Some(album) => println!("{} - {} - {}",
20-
&track.name, &track.artist, album),
21-
None => println!("{} - {}",
22-
&track.name, &track.artist)
19+
Some(album) => println!("{} - {} - {}", &track.name, &track.artist, album),
20+
None => println!("{} - {}", &track.name, &track.artist)
2321
}
2422
}
2523
Ok(())

‎src/subcmds/get_playlists.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use clap::{ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "get-playlists";
66

‎src/subcmds/get_volume.rs

+28-10
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
use clap::{Arg, ArgMatches, ArgGroup, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgGroup, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "get-volume";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Get volume from a player or group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("GROUP").short("g").long("group").takes_value(true).value_name("NAME"))
12-
.arg(Arg::with_name("PLAYER").short("p").long("player").takes_value(true).value_name("NAME"))
11+
.arg(
12+
Arg::with_name("GROUP")
13+
.short("g")
14+
.long("group")
15+
.takes_value(true)
16+
.value_name("NAME")
17+
)
18+
.arg(
19+
Arg::with_name("PLAYER")
20+
.short("p")
21+
.long("player")
22+
.takes_value(true)
23+
.value_name("NAME")
24+
)
1325
.group(ArgGroup::with_name("TARGET").args(&["GROUP", "PLAYER"]))
1426
}
1527

@@ -19,15 +31,21 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
1931
let player_name = matches.value_of("PLAYER");
2032
let household = matches.household(sonos)?;
2133
let targets = sonos.get_groups(&household)?;
22-
for player in targets.players.iter().filter(|player|
34+
for player in targets.players.iter().filter(|player| {
2335
player_name.map_or(group_name.is_none(), |name| name == player.name)
24-
) {
36+
}) {
2537
found = true;
26-
println!("{:?} => {:#?}", player.name, sonos.get_player_volume(&player)?);
38+
println!(
39+
"{:?} => {:#?}",
40+
player.name,
41+
sonos.get_player_volume(&player)?
42+
);
2743
}
28-
for group in targets.groups.iter().filter(|group|
29-
group_name.map_or(player_name.is_none(), |name| name == group.name)
30-
) {
44+
for group in targets
45+
.groups
46+
.iter()
47+
.filter(|group| group_name.map_or(player_name.is_none(), |name| name == group.name))
48+
{
3149
found = true;
3250
println!("{:?} => {:#?}", group.name, sonos.get_group_volume(&group)?);
3351
}

‎src/subcmds/init.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use clap::{ArgMatches, App};
1+
use crate::Result;
2+
use clap::{App, ArgMatches};
23
use oauth2::{ClientId, ClientSecret, RedirectUrl};
34
use ronor::Sonos;
45
use rustyline::Editor;
5-
use crate::Result;
66
use url::Url;
77

88
pub const NAME: &str = "init";
99

1010
pub fn build() -> App<'static, 'static> {
11-
App::new(NAME)
12-
.about("Initialise sonos integration configuration")
11+
App::new(NAME).about("Initialise sonos integration configuration")
1312
}
1413

1514
pub fn run(sonos: &mut Sonos, _matches: &ArgMatches) -> Result<()> {
@@ -21,9 +20,8 @@ pub fn run(sonos: &mut Sonos, _matches: &ArgMatches) -> Result<()> {
2120
let mut console = Editor::<()>::new();
2221
let client_id = ClientId::new(console.readline("Client identifier: ")?);
2322
let client_secret = ClientSecret::new(console.readline("Client secret: ")?);
24-
let redirect_url = RedirectUrl::new(
25-
Url::parse(&console.readline("Redirection URL: ")?)?
26-
);
23+
let redirect_url =
24+
RedirectUrl::new(Url::parse(&console.readline("Redirection URL: ")?)?);
2725
sonos.set_integration_config(client_id, client_secret, redirect_url)?;
2826
println!();
2927
println!("OK, ready to go.");

‎src/subcmds/inventory.rs

+68-35
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,48 @@
1-
use clap::{Arg, ArgMatches, App};
2-
use ronor::{Sonos, Capability, HouseholdId, Player, PlayerId};
31
use crate::Result;
2+
use clap::{App, Arg, ArgMatches};
3+
use ronor::{Capability, HouseholdId, Player, PlayerId, Sonos};
44

55
pub const NAME: &str = "inventory";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Describes available households, groups and logical players")
10-
.arg(Arg::with_name("AUDIO_CLIP").short("c").long("audio-clip")
11-
.help("Limits to players with the audio-clip capability"))
12-
.arg(Arg::with_name("HT_PLAYBACK").short("t").long("ht-playback")
13-
.help("Limits to players with the home theater playback capability"))
14-
.arg(Arg::with_name("LINE_IN").short("l").long("line-in")
15-
.help("Only show players with the line-in capability"))
16-
.arg(Arg::with_name("PLAYERS").long("players")
17-
.help("Only show players"))
18-
.arg(Arg::with_name("HOUSEHOLD").long("household-id").takes_value(true).value_name("IDENTIFIER")
19-
.help("Limits output to a specific household"))
10+
.arg(
11+
Arg::with_name("AUDIO_CLIP")
12+
.short("c")
13+
.long("audio-clip")
14+
.help("Limits to players with the audio-clip capability")
15+
)
16+
.arg(
17+
Arg::with_name("HT_PLAYBACK")
18+
.short("t")
19+
.long("ht-playback")
20+
.help("Limits to players with the home theater playback capability")
21+
)
22+
.arg(
23+
Arg::with_name("LINE_IN")
24+
.short("l")
25+
.long("line-in")
26+
.help("Only show players with the line-in capability")
27+
)
28+
.arg(
29+
Arg::with_name("PLAYERS")
30+
.long("players")
31+
.help("Only show players")
32+
)
33+
.arg(
34+
Arg::with_name("HOUSEHOLD")
35+
.long("household-id")
36+
.takes_value(true)
37+
.value_name("IDENTIFIER")
38+
.help("Limits output to a specific household")
39+
)
2040
}
2141

2242
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
23-
let household_id = matches.value_of("HOUSEHOLD").map(|id|
24-
HouseholdId::new(id.to_string())
25-
);
43+
let household_id = matches
44+
.value_of("HOUSEHOLD")
45+
.map(|id| HouseholdId::new(id.to_string()));
2646
let audio_clip = if matches.is_present("AUDIO_CLIP") {
2747
Some(Capability::AudioClip)
2848
} else {
@@ -38,32 +58,45 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
3858
} else {
3959
None
4060
};
41-
for household in sonos.get_households()?.iter().filter(|household|
42-
household_id.as_ref().map_or(true, |household_id|
43-
household_id == &household.id
44-
)
45-
) {
61+
for household in sonos.get_households()?.iter().filter(|household| {
62+
household_id
63+
.as_ref()
64+
.map_or(true, |household_id| household_id == &household.id)
65+
}) {
4666
if household_id.is_none() {
4767
println!("Household: {}", household.id);
4868
}
4969
let targets = sonos.get_groups(&household)?;
50-
fn find_player<'a>(players: &'a [Player], player_id: &PlayerId) -> Option<&'a Player> {
70+
fn find_player<'a>(
71+
players: &'a [Player],
72+
player_id: &PlayerId
73+
) -> Option<&'a Player> {
5174
players.iter().find(|player| &player.id == player_id)
5275
}
53-
if matches.is_present("PLAYERS") || audio_clip.is_some() || ht_playback.is_some() || line_in.is_some() {
54-
for player in targets.players.iter().filter(|player|
55-
audio_clip.as_ref().map_or(true, |capability|
56-
player.capabilities.contains(&capability)
57-
)
58-
).filter(|player|
59-
ht_playback.as_ref().map_or(true, |capability|
60-
player.capabilities.contains(&capability)
61-
)
62-
).filter(|player|
63-
line_in.as_ref().map_or(true, |capability|
64-
player.capabilities.contains(&capability)
65-
)
66-
) {
76+
if matches.is_present("PLAYERS")
77+
|| audio_clip.is_some()
78+
|| ht_playback.is_some()
79+
|| line_in.is_some()
80+
{
81+
for player in targets
82+
.players
83+
.iter()
84+
.filter(|player| {
85+
audio_clip
86+
.as_ref()
87+
.map_or(true, |capability| player.capabilities.contains(&capability))
88+
})
89+
.filter(|player| {
90+
ht_playback
91+
.as_ref()
92+
.map_or(true, |capability| player.capabilities.contains(&capability))
93+
})
94+
.filter(|player| {
95+
line_in
96+
.as_ref()
97+
.map_or(true, |capability| player.capabilities.contains(&capability))
98+
})
99+
{
67100
println!("{}", player.name);
68101
}
69102
} else {

‎src/subcmds/load_audio_clip.rs

+71-27
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,67 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44
use url::Url;
55

66
pub const NAME: &str = "load-audio-clip";
77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Schedule an audio clip to play on a particular player")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("NAME").default_value("ronor clip")
12-
.short("n").long("name").takes_value(true))
13-
.arg(Arg::with_name("APP_ID").default_value("guru.blind").value_name("STRING")
14-
.short("i").long("app-id").takes_value(true))
15-
.arg(Arg::with_name("CLIP_TYPE").short("t").long("type").takes_value(true)
16-
.possible_values(&["Chime", "Custom"]))
17-
.arg(Arg::with_name("PRIORITY").short("p").long("priority").takes_value(true)
18-
.possible_values(&["Low", "High"]))
19-
.arg(Arg::with_name("VOLUME").short("v").long("volume").takes_value(true)
20-
.help("Volume in percent (0-100)"))
21-
.arg(Arg::with_name("HTTP_AUTHORIZATION")
22-
.short("a").long("http-authorization").takes_value(true).value_name("STRING")
23-
.help("HTTP Authorization string"))
24-
.arg(Arg::with_name("PLAYER").required(true)
25-
.help("Name of the player"))
26-
.arg(Arg::with_name("URL").required(true)
27-
.help("Location of the audio clip"))
11+
.arg(
12+
Arg::with_name("NAME")
13+
.default_value("ronor clip")
14+
.short("n")
15+
.long("name")
16+
.takes_value(true)
17+
)
18+
.arg(
19+
Arg::with_name("APP_ID")
20+
.default_value("guru.blind")
21+
.value_name("STRING")
22+
.short("i")
23+
.long("app-id")
24+
.takes_value(true)
25+
)
26+
.arg(
27+
Arg::with_name("CLIP_TYPE")
28+
.short("t")
29+
.long("type")
30+
.takes_value(true)
31+
.possible_values(&["Chime", "Custom"])
32+
)
33+
.arg(
34+
Arg::with_name("PRIORITY")
35+
.short("p")
36+
.long("priority")
37+
.takes_value(true)
38+
.possible_values(&["Low", "High"])
39+
)
40+
.arg(
41+
Arg::with_name("VOLUME")
42+
.short("v")
43+
.long("volume")
44+
.takes_value(true)
45+
.help("Volume in percent (0-100)")
46+
)
47+
.arg(
48+
Arg::with_name("HTTP_AUTHORIZATION")
49+
.short("a")
50+
.long("http-authorization")
51+
.takes_value(true)
52+
.value_name("STRING")
53+
.help("HTTP Authorization string")
54+
)
55+
.arg(
56+
Arg::with_name("PLAYER")
57+
.required(true)
58+
.help("Name of the player")
59+
)
60+
.arg(
61+
Arg::with_name("URL")
62+
.required(true)
63+
.help("Location of the audio clip")
64+
)
2865
}
2966

3067
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
@@ -33,22 +70,29 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
3370
let player = matches.player(&targets.players)?;
3471
let url = value_t!(matches, "URL", Url).unwrap();
3572
if url.has_host() {
36-
sonos.load_audio_clip(&player,
73+
sonos.load_audio_clip(
74+
&player,
3775
matches.value_of("APP_ID").unwrap(),
3876
matches.value_of("NAME").unwrap(),
3977
match matches.value_of("CLIP_TYPE") {
40-
Some(s) => Some(s.parse::<>()?),
78+
Some(s) => Some(s.parse()?),
4179
None => None
42-
}, match matches.value_of("PRIORITY") {
43-
Some(s) => Some(s.parse::<>()?),
80+
},
81+
match matches.value_of("PRIORITY") {
82+
Some(s) => Some(s.parse()?),
4483
None => None
45-
}, match matches.value_of("VOLUME") {
46-
Some(s) => Some(s.parse::<>()?),
84+
},
85+
match matches.value_of("VOLUME") {
86+
Some(s) => Some(s.parse()?),
4787
None => None
48-
}, matches.value_of("HTTP_AUTHORIZATION"), Some(&url)
88+
},
89+
matches.value_of("HTTP_AUTHORIZATION"),
90+
Some(&url)
4991
)?;
5092
} else {
51-
return Err("The URL you provided does not look like Sonos will be able to reach it".into());
93+
return Err(
94+
"The URL you provided does not look like Sonos will be able to reach it".into()
95+
);
5296
}
5397
Ok(())
5498
}

‎src/subcmds/load_favorite.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "load-favorite";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Load the specified favorite in a group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("PLAY").short("p").long("play")
12-
.help("Automatically start playback"))
11+
.arg(
12+
Arg::with_name("PLAY")
13+
.short("p")
14+
.long("play")
15+
.help("Automatically start playback")
16+
)
1317
.args(&crate::play_modes_args())
1418
.arg(Arg::with_name("FAVORITE").required(true))
1519
.arg(Arg::with_name("GROUP").required(true))
@@ -21,7 +25,11 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
2125
let targets = sonos.get_groups(&household)?;
2226
let group = matches.group(&targets.groups)?;
2327
let play_on_completion = matches.is_present("PLAY");
24-
sonos.load_favorite(&group,
25-
&favorite, play_on_completion, matches.play_modes().as_ref())?;
28+
sonos.load_favorite(
29+
&group,
30+
&favorite,
31+
play_on_completion,
32+
matches.play_modes().as_ref()
33+
)?;
2634
Ok(())
2735
}

‎src/subcmds/load_home_theater_playback.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "load-home-theater-playback";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Signal a player to switch to its TV input (optical or HDMI)")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("PLAYER").required(true)
12-
.help("Name of the player"))
11+
.arg(
12+
Arg::with_name("PLAYER")
13+
.required(true)
14+
.help("Name of the player")
15+
)
1316
}
1417

1518
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {

‎src/subcmds/load_line_in.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "load-line-in";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Change the given group to the line-in source of a specified player")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("PLAY").short("p").long("play")
12-
.help("Automatically start playback"))
13-
.arg(Arg::with_name("GROUP").required(true)
14-
.help("Name of the group"))
15-
.arg(Arg::with_name("PLAYER")
16-
.help("Name of the player"))
11+
.arg(
12+
Arg::with_name("PLAY")
13+
.short("p")
14+
.long("play")
15+
.help("Automatically start playback")
16+
)
17+
.arg(
18+
Arg::with_name("GROUP")
19+
.required(true)
20+
.help("Name of the group")
21+
)
22+
.arg(Arg::with_name("PLAYER").help("Name of the player"))
1723
}
1824

1925
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {

‎src/subcmds/load_playlist.rs

+24-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "load-playlist";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Load the specified playlist in a group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("PLAY").short("p").long("play")
12-
.help("Automatically start playback"))
11+
.arg(
12+
Arg::with_name("PLAY")
13+
.short("p")
14+
.long("play")
15+
.help("Automatically start playback")
16+
)
1317
.args(&crate::play_modes_args())
14-
.arg(Arg::with_name("PLAYLIST").required(true)
15-
.help("The name of the playlist to load"))
16-
.arg(Arg::with_name("GROUP").required(true)
17-
.help("The name of the group to load the playlist in"))
18+
.arg(
19+
Arg::with_name("PLAYLIST")
20+
.required(true)
21+
.help("The name of the playlist to load")
22+
)
23+
.arg(
24+
Arg::with_name("GROUP")
25+
.required(true)
26+
.help("The name of the group to load the playlist in")
27+
)
1828
}
1929

2030
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
@@ -23,7 +33,11 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
2333
let targets = sonos.get_groups(&household)?;
2434
let group = matches.group(&targets.groups)?;
2535
let play_on_completion = matches.is_present("PLAY");
26-
sonos.load_playlist(&group,
27-
&playlist, play_on_completion, matches.play_modes().as_ref())?;
36+
sonos.load_playlist(
37+
&group,
38+
&playlist,
39+
play_on_completion,
40+
matches.play_modes().as_ref()
41+
)?;
2842
Ok(())
2943
}

‎src/subcmds/login.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::Result;
2+
use clap::{App, Arg, ArgMatches};
23
use oauth2::AuthorizationCode;
34
use ronor::Sonos;
45
use rustyline::Editor;
5-
use std::process::{Command};
6-
use crate::Result;
6+
use std::process::Command;
77

88
pub const NAME: &str = "login";
99

1010
pub fn build() -> App<'static, 'static> {
1111
App::new(NAME)
1212
.about("Login with your sonos user account and authorize ronor")
13-
.arg(Arg::with_name("BROWSER").default_value("lynx")
14-
.help("The browser to use to login to Sonos"))
13+
.arg(
14+
Arg::with_name("BROWSER")
15+
.default_value("lynx")
16+
.help("The browser to use to login to Sonos")
17+
)
1518
}
1619

1720
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
1821
let (auth_url, csrf_token) = sonos.authorization_url()?;
1922
let _browser = Command::new(matches.value_of("BROWSER").unwrap())
2023
.arg(auth_url.as_str())
21-
.status().expect("Failed to fire up browser.");
24+
.status()
25+
.expect("Failed to fire up browser.");
2226
println!("Token: {}", csrf_token.secret());
2327
let mut console = Editor::<()>::new();
2428
let code = console.readline("Code: ")?;

‎src/subcmds/modify_group.rs

+31-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
1-
use clap::{Arg, ArgMatches, App};
2-
use ronor::{Sonos, Player, PlayerId};
3-
use crate::{Result, ErrorKind, ArgMatchesExt};
1+
use crate::{ArgMatchesExt, ErrorKind, Result};
2+
use clap::{App, Arg, ArgMatches};
3+
use ronor::{Player, PlayerId, Sonos};
44

55
pub const NAME: &str = "modify-group";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Add or remove logical players to/from a group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("GROUP").required(true).takes_value(true)
12-
.help("The name of the group to modify"))
13-
.arg(Arg::with_name("ADD").short("a").long("add")
14-
.takes_value(true).value_name("PLAYER_NAME").multiple(true)
15-
.help("Names of the logical players to add"))
16-
.arg(Arg::with_name("REMOVE").short("r").long("remove")
17-
.takes_value(true).value_name("PLAYER_NAME").multiple(true)
18-
.help("Names of the logical players to remove"))
11+
.arg(
12+
Arg::with_name("GROUP")
13+
.required(true)
14+
.takes_value(true)
15+
.help("The name of the group to modify")
16+
)
17+
.arg(
18+
Arg::with_name("ADD")
19+
.short("a")
20+
.long("add")
21+
.takes_value(true)
22+
.value_name("PLAYER_NAME")
23+
.multiple(true)
24+
.help("Names of the logical players to add")
25+
)
26+
.arg(
27+
Arg::with_name("REMOVE")
28+
.short("r")
29+
.long("remove")
30+
.takes_value(true)
31+
.value_name("PLAYER_NAME")
32+
.multiple(true)
33+
.help("Names of the logical players to remove")
34+
)
1935
}
2036

2137
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
@@ -24,15 +40,15 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
2440
let group = matches.group(&targets.groups)?;
2541
let player_ids_to_add = player_ids(matches.values_of("ADD"), &targets.players)?;
2642
let player_ids_to_remove = player_ids(matches.values_of("REMOVE"), &targets.players)?;
27-
let modified_group = sonos.modify_group_members(&group,
28-
&player_ids_to_add, &player_ids_to_remove
29-
)?;
43+
let modified_group =
44+
sonos.modify_group_members(&group, &player_ids_to_add, &player_ids_to_remove)?;
3045
println!("{} -> {}", group.name, modified_group.name);
3146
Ok(())
3247
}
3348

3449
fn player_ids<'a, 'b, I: Iterator<Item = &'a str>>(
35-
names: Option<I>, players: &'b [Player]
50+
names: Option<I>,
51+
players: &'b [Player]
3652
) -> Result<Vec<&'b PlayerId>> {
3753
let mut ids = Vec::new();
3854
if let Some(names) = names {

‎src/subcmds/now_playing.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
use clap::{Arg, ArgMatches, App};
2-
use ronor::{Sonos, PlaybackState};
3-
use crate::{Result, ErrorKind};
1+
use crate::{ErrorKind, Result};
2+
use clap::{App, Arg, ArgMatches};
3+
use ronor::{PlaybackState, Sonos};
44

55
pub const NAME: &str = "now-playing";
66

77
pub fn build() -> App<'static, 'static> {
8-
App::new(NAME).visible_alias("np")
8+
App::new(NAME)
9+
.visible_alias("np")
910
.about("Describes what is currently playing")
1011
.arg(Arg::with_name("GROUP"))
1112
}
@@ -14,15 +15,20 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
1415
let group_name = matches.value_of("GROUP");
1516
let mut found = false;
1617
for household in sonos.get_households()?.iter() {
17-
for group in sonos.get_groups(&household)?.groups.iter().filter(|group|
18-
group_name.map_or(true, |name| name == group.name)
19-
) {
18+
for group in sonos
19+
.get_groups(&household)?
20+
.groups
21+
.iter()
22+
.filter(|group| group_name.map_or(true, |name| name == group.name))
23+
{
2024
found = true;
2125
if group.playback_state == PlaybackState::Playing {
2226
let metadata_status = sonos.get_metadata_status(&group)?;
2327
let mut parts = Vec::new();
2428
if let Some(container) = &metadata_status.container {
25-
if container.type_.is_some() && container.type_.as_ref().unwrap() == "linein.homeTheater" {
29+
if container.type_.is_some()
30+
&& container.type_.as_ref().unwrap() == "linein.homeTheater"
31+
{
2632
parts.push("Home theater");
2733
} else {
2834
if let Some(name) = &container.name {

‎src/subcmds/pause.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, ErrorKind, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ErrorKind, ArgMatchesExt};
44

55
pub const NAME: &str = "pause";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Pause playback for the given group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("GROUP")
12-
.help("Name of the group"))
11+
.arg(Arg::with_name("GROUP").help("Name of the group"))
1312
}
1413

1514
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {

‎src/subcmds/play.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, ErrorKind, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ErrorKind, ArgMatchesExt};
44

55
pub const NAME: &str = "play";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Start playback for the given group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("GROUP")
12-
.help("Name of the group"))
11+
.arg(Arg::with_name("GROUP").help("Name of the group"))
1312
}
1413

1514
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {

‎src/subcmds/seek.rs

+32-13
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result, ResultExt};
2+
use clap::{App, Arg, ArgMatches};
23
use humantime::parse_duration;
34
use ronor::Sonos;
4-
use crate::{Result, ResultExt, ArgMatchesExt};
55

66
pub const NAME: &str = "seek";
77

88
pub fn build() -> App<'static, 'static> {
99
App::new(NAME)
1010
.about("Go to a specific position in the current track")
1111
.arg(crate::household_arg())
12-
.arg(Arg::with_name("FORWARD").short("f").long("forward").conflicts_with("BACKWARD")
13-
.help("Seek forward relative to current position"))
14-
.arg(Arg::with_name("BACKWARD").short("b").long("backward").conflicts_with("FORWARD")
15-
.help("Seek backward relative to current position"))
16-
.arg(Arg::with_name("TIME").required(true)
17-
.help("Time specification (example: 2m3s)"))
18-
.arg(Arg::with_name("GROUP").required(true)
19-
.help("Name of the group"))
12+
.arg(
13+
Arg::with_name("FORWARD")
14+
.short("f")
15+
.long("forward")
16+
.conflicts_with("BACKWARD")
17+
.help("Seek forward relative to current position")
18+
)
19+
.arg(
20+
Arg::with_name("BACKWARD")
21+
.short("b")
22+
.long("backward")
23+
.conflicts_with("FORWARD")
24+
.help("Seek backward relative to current position")
25+
)
26+
.arg(
27+
Arg::with_name("TIME")
28+
.required(true)
29+
.help("Time specification (example: 2m3s)")
30+
)
31+
.arg(
32+
Arg::with_name("GROUP")
33+
.required(true)
34+
.help("Name of the group")
35+
)
2036
}
2137

2238
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
@@ -27,14 +43,17 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
2743
let forward = matches.is_present("BACKWARD");
2844
let relative = backward || forward;
2945
let time = matches.value_of("TIME").unwrap();
30-
let duration = parse_duration(time).chain_err(|| "Failed to parse time specification")?;
46+
let duration =
47+
parse_duration(time).chain_err(|| "Failed to parse time specification")?;
3148
if relative {
32-
sonos.seek_relative(&group,
49+
sonos.seek_relative(
50+
&group,
3351
if backward {
3452
-(duration.as_millis() as i128)
3553
} else {
3654
duration.as_millis() as i128
37-
}, None
55+
},
56+
None
3857
)
3958
} else {
4059
sonos.seek(&group, duration.as_millis(), None)

‎src/subcmds/set_mute.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use clap::{Arg, ArgMatches, ArgGroup, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgGroup, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "set-mute";
66

@@ -9,11 +9,25 @@ pub fn build() -> App<'static, 'static> {
99
.about("Set mute state for a group or player")
1010
.arg(crate::household_arg())
1111
.arg(Arg::with_name("UNMUTE").short("u").long("unmute"))
12-
.arg(Arg::with_name("GROUP").short("g").long("group")
13-
.takes_value(true).value_name("NAME"))
14-
.arg(Arg::with_name("PLAYER").short("p").long("player")
15-
.takes_value(true).value_name("NAME"))
16-
.group(ArgGroup::with_name("TARGET").args(&["GROUP", "PLAYER"]).required(true))
12+
.arg(
13+
Arg::with_name("GROUP")
14+
.short("g")
15+
.long("group")
16+
.takes_value(true)
17+
.value_name("NAME")
18+
)
19+
.arg(
20+
Arg::with_name("PLAYER")
21+
.short("p")
22+
.long("player")
23+
.takes_value(true)
24+
.value_name("NAME")
25+
)
26+
.group(
27+
ArgGroup::with_name("TARGET")
28+
.args(&["GROUP", "PLAYER"])
29+
.required(true)
30+
)
1731
}
1832

1933
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {

‎src/subcmds/set_volume.rs

+36-17
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,43 @@
1-
use clap::{Arg, ArgMatches, ArgGroup, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgGroup, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "set-volume";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Set volume for a group or player")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("RELATIVE").short("r").long("relative")
12-
.help("Indicates that the volume should be interpreted as relative"))
13-
.arg(Arg::with_name("GROUP")
14-
.short("g").long("group")
15-
.takes_value(true).value_name("NAME"))
16-
.arg(Arg::with_name("PLAYER")
17-
.short("p").long("player")
18-
.takes_value(true).value_name("NAME"))
19-
.group(ArgGroup::with_name("TARGET").args(&["GROUP", "PLAYER"]).required(true))
20-
.arg(Arg::with_name("VOLUME").required(true)
21-
.help("Volume in percent"))
11+
.arg(
12+
Arg::with_name("RELATIVE")
13+
.short("r")
14+
.long("relative")
15+
.help("Indicates that the volume should be interpreted as relative")
16+
)
17+
.arg(
18+
Arg::with_name("GROUP")
19+
.short("g")
20+
.long("group")
21+
.takes_value(true)
22+
.value_name("NAME")
23+
)
24+
.arg(
25+
Arg::with_name("PLAYER")
26+
.short("p")
27+
.long("player")
28+
.takes_value(true)
29+
.value_name("NAME")
30+
)
31+
.group(
32+
ArgGroup::with_name("TARGET")
33+
.args(&["GROUP", "PLAYER"])
34+
.required(true)
35+
)
36+
.arg(
37+
Arg::with_name("VOLUME")
38+
.required(true)
39+
.help("Volume in percent")
40+
)
2241
}
2342

2443
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
@@ -29,16 +48,16 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
2948
if matches.is_present("GROUP") {
3049
let group = matches.group(&targets.groups)?;
3150
if relative {
32-
sonos.set_relative_group_volume(&group, volume.parse::<>()?)
51+
sonos.set_relative_group_volume(&group, volume.parse()?)
3352
} else {
34-
sonos.set_group_volume(&group, volume.parse::<>()?)
53+
sonos.set_group_volume(&group, volume.parse()?)
3554
}
3655
} else {
3756
let player = matches.player(&targets.players)?;
3857
if relative {
39-
sonos.set_relative_player_volume(&player, volume.parse::<>()?)
58+
sonos.set_relative_player_volume(&player, volume.parse()?)
4059
} else {
41-
sonos.set_player_volume(&player, volume.parse::<>()?)
60+
sonos.set_player_volume(&player, volume.parse()?)
4261
}
4362
}?;
4463
Ok(())

‎src/subcmds/skip.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
use clap::{Arg, ArgMatches, ArgGroup, App};
1+
use crate::{ArgMatchesExt, Result};
2+
use clap::{App, Arg, ArgGroup, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ArgMatchesExt};
44

55
pub const NAME: &str = "skip";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Go to next or previous track in the given group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("NEXT").short("n").long("next-track")
12-
.help("Skip to next track"))
13-
.arg(Arg::with_name("PREVIOUS").short("p").long("previous-track")
14-
.help("Skip to previous track"))
11+
.arg(
12+
Arg::with_name("NEXT")
13+
.short("n")
14+
.long("next-track")
15+
.help("Skip to next track")
16+
)
17+
.arg(
18+
Arg::with_name("PREVIOUS")
19+
.short("p")
20+
.long("previous-track")
21+
.help("Skip to previous track")
22+
)
1523
.group(ArgGroup::with_name("DIRECTION").args(&["NEXT", "PREVIOUS"]))
1624
.arg(Arg::with_name("GROUP").required(true))
1725
}

‎src/subcmds/speak.rs

+86-38
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use clap::{Arg, ArgGroup, ArgMatches, App};
1+
use crate::{ArgMatchesExt, Result, ResultExt};
2+
use clap::{App, Arg, ArgGroup, ArgMatches};
23
use ronor::Sonos;
34
use scraper::{Html, Selector};
45
use std::collections::HashMap;
56
use std::io::Write;
67
use std::process::{Command, Stdio};
7-
use crate::{Result, ResultExt, ArgMatchesExt};
88
use url::Url;
99

1010
pub const NAME: &str = "speak";
@@ -13,36 +13,58 @@ pub fn build() -> App<'static, 'static> {
1313
App::new(NAME)
1414
.about("Send synthetic speech to a player")
1515
.arg(crate::household_arg())
16-
.arg(Arg::with_name("SCRAPE").long("scrape")
17-
.takes_value(true).value_name("URI")
18-
.help("Scrape a specific web resource instead of taking text from STDIN"))
19-
.arg(Arg::with_name("LANGUAGE")
20-
.short("l").long("language")
21-
.takes_value(true)
22-
.help("What language is the text coming from STDIN"))
16+
.arg(
17+
Arg::with_name("SCRAPE")
18+
.long("scrape")
19+
.takes_value(true)
20+
.value_name("URI")
21+
.help("Scrape a specific web resource instead of taking text from STDIN")
22+
)
23+
.arg(
24+
Arg::with_name("LANGUAGE")
25+
.short("l")
26+
.long("language")
27+
.takes_value(true)
28+
.help("What language is the text coming from STDIN")
29+
)
2330
.group(ArgGroup::with_name("SOURCE").args(&["SCRAPE", "LANGUAGE"]))
24-
.arg(Arg::with_name("WORDS_PER_MINUTE").short("s").long("speed").takes_value(true)
25-
.default_value("250"))
26-
.arg(Arg::with_name("VOLUME").short("v").long("volume").takes_value(true)
27-
.default_value("75"))
28-
.arg(Arg::with_name("PLAYER").required(true)
29-
.help("Name of the player"))
31+
.arg(
32+
Arg::with_name("WORDS_PER_MINUTE")
33+
.short("s")
34+
.long("speed")
35+
.takes_value(true)
36+
.default_value("250")
37+
)
38+
.arg(
39+
Arg::with_name("VOLUME")
40+
.short("v")
41+
.long("volume")
42+
.takes_value(true)
43+
.default_value("75")
44+
)
45+
.arg(
46+
Arg::with_name("PLAYER")
47+
.required(true)
48+
.help("Name of the player")
49+
)
3050
}
3151

3252
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
3353
let household = matches.household(sonos)?;
3454
let targets = sonos.get_groups(&household)?;
3555
let player = matches.player(&targets.players)?;
36-
let mut args = vec![ String::from("-w")
37-
, String::from("/dev/stdout")
38-
, String::from("--stdin")];
56+
let mut args = vec![
57+
String::from("-w"),
58+
String::from("/dev/stdout"),
59+
String::from("--stdin"),
60+
];
3961
let text = match matches.value_of("SCRAPE") {
4062
Some(uri) => match scrapers().get(uri) {
4163
Some(scraper) => {
4264
let (language, text) = scraper(uri)?;
4365
args.extend(vec![String::from("-v"), language]);
4466
Some(text)
45-
},
67+
}
4668
None => return Err("Scrape URI not supported".into())
4769
},
4870
None => None
@@ -59,15 +81,18 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
5981
}
6082

6183
let espeak = if text.is_some() {
62-
Command::new("espeak").args(args)
84+
Command::new("espeak")
85+
.args(args)
6386
.stdin(Stdio::piped())
6487
.stdout(Stdio::piped())
6588
.spawn()
6689
} else {
67-
Command::new("espeak").args(args)
90+
Command::new("espeak")
91+
.args(args)
6892
.stdout(Stdio::piped())
6993
.spawn()
70-
}.chain_err(|| "Failed to spawn 'espeak'")?;
94+
}
95+
.chain_err(|| "Failed to spawn 'espeak'")?;
7196
if let Some(text) = text {
7297
espeak.stdin.unwrap().write_all(text.as_bytes())?;
7398
print!("{}", &text);
@@ -79,48 +104,60 @@ pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {
79104
.chain_err(|| "Failed to spawn 'ffmpeg'")?
80105
.stdout;
81106
let client = reqwest::Client::new();
82-
let url = client.put("https://transfer.sh/espeak.mp3").body(mp3)
107+
let url = client
108+
.put("https://transfer.sh/espeak.mp3")
109+
.body(mp3)
83110
.send()
84111
.chain_err(|| "Failed to send audio clip to transfer.sh")?
85112
.error_for_status()
86113
.chain_err(|| "Failed to upload audio clip to transfer.sh")?
87114
.text()?;
88115
let url = Url::parse(&url).chain_err(|| "Failed to parse transfer.sh reply")?;
89-
sonos.load_audio_clip(&player,
90-
"guru.blind", "ping", None, None, None, None, Some(&url)
116+
sonos.load_audio_clip(
117+
&player,
118+
"guru.blind",
119+
"ping",
120+
None,
121+
None,
122+
None,
123+
None,
124+
Some(&url)
91125
)?;
92126
Ok(())
93127
}
94128

95129
type Scraper = fn(&str) -> Result<(String, String)>;
96130

97131
fn parse(url: &str) -> Result<Html> {
98-
Ok(Html::parse_document(&reqwest::get(url)?.error_for_status()?.text()?))
132+
Ok(Html::parse_document(
133+
&reqwest::get(url)?.error_for_status()?.text()?
134+
))
99135
}
100136

101137
fn wetter_orf_at(uri: &str) -> Result<(String, String)> {
102138
let html = parse(&format!("https://{}/prognose", uri))?;
103-
let selector = Selector::parse(
104-
"div.fulltextWrapper > h2, div.fulltextWrapper > p"
105-
).unwrap();
139+
let selector =
140+
Selector::parse("div.fulltextWrapper > h2, div.fulltextWrapper > p").unwrap();
106141
let mut s = String::new();
107142
let mut first_line = true;
108143
for element in html.select(&selector) {
109144
let is_h2 = element.value().name() == "h2";
110-
if is_h2 && !first_line { s += "\n"; }
145+
if is_h2 && !first_line {
146+
s += "\n";
147+
}
111148
s += &element.text().collect::<Vec<_>>().join("");
112149
s += "\n";
113-
if is_h2 { s += "\n"; }
150+
if is_h2 {
151+
s += "\n";
152+
}
114153
first_line = false;
115154
}
116155
Ok(("de".to_string(), s))
117156
}
118157

119158
fn zamg_ac_at(uri: &str) -> Result<(String, String)> {
120159
let html = parse(&format!("https://www.{}/", uri))?;
121-
let selector = Selector::parse(
122-
"div#prognosenText > p"
123-
).unwrap();
160+
let selector = Selector::parse("div#prognosenText > p").unwrap();
124161
let mut s = String::new();
125162
for element in html.select(&selector) {
126163
s += &element.text().collect::<Vec<_>>().join("");
@@ -131,11 +168,22 @@ fn zamg_ac_at(uri: &str) -> Result<(String, String)> {
131168

132169
fn scrapers() -> HashMap<String, Scraper> {
133170
let mut m: HashMap<_, Scraper> = HashMap::new();
134-
for region in &["burgenland", "kaernten", "niederoesterreich",
135-
"oberoesterreich", "salzburg", "steiermark",
136-
"tirol", "vorarlberg", "wien"] {
171+
for region in &[
172+
"burgenland",
173+
"kaernten",
174+
"niederoesterreich",
175+
"oberoesterreich",
176+
"salzburg",
177+
"steiermark",
178+
"tirol",
179+
"vorarlberg",
180+
"wien"
181+
] {
137182
m.insert("wetter.orf.at/".to_string() + region, wetter_orf_at);
138-
m.insert("zamg.ac.at/cms/de/wetter/wetter-oesterreich/".to_string() + region, zamg_ac_at);
183+
m.insert(
184+
"zamg.ac.at/cms/de/wetter/wetter-oesterreich/".to_string() + region,
185+
zamg_ac_at
186+
);
139187
}
140188
m
141189
}

‎src/subcmds/toggle_play_pause.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use clap::{Arg, ArgMatches, App};
1+
use crate::{ArgMatchesExt, ErrorKind, Result};
2+
use clap::{App, Arg, ArgMatches};
23
use ronor::Sonos;
3-
use crate::{Result, ErrorKind, ArgMatchesExt};
44

55
pub const NAME: &str = "toggle-play-pause";
66

77
pub fn build() -> App<'static, 'static> {
88
App::new(NAME)
99
.about("Toggle the playback state of the given group")
1010
.arg(crate::household_arg())
11-
.arg(Arg::with_name("GROUP")
12-
.help("Name of the group"))
11+
.arg(Arg::with_name("GROUP").help("Name of the group"))
1312
}
1413

1514
pub fn run(sonos: &mut Sonos, matches: &ArgMatches) -> Result<()> {

0 commit comments

Comments
 (0)
Please sign in to comment.