aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-14 23:00:15 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-15 12:45:54 -0500
commitacd1173848b4db1c733af7d3f53d24aab900b542 (patch)
treeb0a1151e390c6063169325bc4520d7b79ac50d9e
parentcc20037ff21a259419c7c00f6fce82ded3888d1e (diff)
downloadrbw-acd1173848b4db1c733af7d3f53d24aab900b542.tar.gz
rbw-acd1173848b4db1c733af7d3f53d24aab900b542.zip
clippy
-rw-r--r--src/actions.rs6
-rw-r--r--src/api.rs78
-rw-r--r--src/bin/rbw-agent/actions.rs46
-rw-r--r--src/bin/rbw-agent/agent.rs10
-rw-r--r--src/bin/rbw-agent/main.rs19
-rw-r--r--src/bin/rbw-agent/sock.rs2
-rw-r--r--src/bin/rbw/actions.rs10
-rw-r--r--src/bin/rbw/commands.rs251
-rw-r--r--src/bin/rbw/main.rs17
-rw-r--r--src/config.rs14
-rw-r--r--src/db.rs3
-rw-r--r--src/dirs.rs10
-rw-r--r--src/edit.rs1
-rw-r--r--src/lib.rs16
-rw-r--r--src/locked.rs14
-rw-r--r--src/pinentry.rs6
-rw-r--r--src/protocol.rs1
-rw-r--r--src/pwgen.rs3
18 files changed, 278 insertions, 229 deletions
diff --git a/src/actions.rs b/src/actions.rs
index 02ec854..b16e6f2 100644
--- a/src/actions.rs
+++ b/src/actions.rs
@@ -39,13 +39,13 @@ pub async fn login(
Ok((access_token, refresh_token, iterations, protected_key))
}
-pub async fn unlock(
+pub fn unlock<S: std::hash::BuildHasher>(
email: &str,
password: &crate::locked::Password,
iterations: u32,
protected_key: &str,
protected_private_key: &str,
- protected_org_keys: &std::collections::HashMap<String, String>,
+ protected_org_keys: &std::collections::HashMap<String, String, S>,
) -> Result<(
crate::locked::Keys,
std::collections::HashMap<String, crate::locked::Keys>,
@@ -148,7 +148,7 @@ fn add_once(
let config = crate::config::Config::load()?;
let client =
crate::api::Client::new(&config.base_url(), &config.identity_url());
- client.add(access_token, name, data, notes, folder_id.as_deref())?;
+ client.add(access_token, name, data, notes, folder_id)?;
Ok(())
}
diff --git a/src/api.rs b/src/api.rs
index 14c11fd..fc3912a 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -1,3 +1,7 @@
+// serde_repr generates some as conversions that we can't seem to silence from
+// here, unfortunately
+#![allow(clippy::as_conversions)]
+
use crate::prelude::*;
use crate::json::{
@@ -138,8 +142,6 @@ struct PreloginReq {
#[derive(serde::Deserialize, Debug)]
struct PreloginRes {
- #[serde(rename = "Kdf")]
- kdf: u32,
#[serde(rename = "KdfIterations")]
kdf_iterations: u32,
}
@@ -169,8 +171,6 @@ struct ConnectPasswordReq {
#[derive(serde::Deserialize, Debug)]
struct ConnectPasswordRes {
access_token: String,
- expires_in: u32,
- token_type: String,
refresh_token: String,
#[serde(rename = "Key")]
key: String,
@@ -202,9 +202,6 @@ struct ConnectRefreshTokenReq {
#[derive(serde::Deserialize, Debug)]
struct ConnectRefreshTokenRes {
access_token: String,
- expires_in: u32,
- token_type: String,
- refresh_token: String,
}
#[derive(serde::Deserialize, Debug)]
@@ -253,32 +250,37 @@ impl SyncResCipher {
if self.deleted_date.is_some() {
return None;
}
- let history = if let Some(history) = &self.password_history {
- history
- .iter()
- .filter_map(|entry| {
- // Gets rid of entries with a non-existent password
- entry.password.clone().map(|p| crate::db::HistoryEntry {
- last_used_date: entry.last_used_date.clone(),
- password: p,
- })
- })
- .collect()
- } else {
- vec![]
- };
+ let history =
+ self.password_history
+ .as_ref()
+ .map_or_else(Vec::new, |history| {
+ history
+ .iter()
+ .filter_map(|entry| {
+ // Gets rid of entries with a non-existent
+ // password
+ entry.password.clone().map(|p| {
+ crate::db::HistoryEntry {
+ last_used_date: entry
+ .last_used_date
+ .clone(),
+ password: p,
+ }
+ })
+ })
+ .collect()
+ });
- let (folder, folder_id) = if let Some(folder_id) = &self.folder_id {
- let mut folder_name = None;
- for folder in folders {
- if &folder.id == folder_id {
- folder_name = Some(folder.name.clone());
+ let (folder, folder_id) =
+ self.folder_id.as_ref().map_or((None, None), |folder_id| {
+ let mut folder_name = None;
+ for folder in folders {
+ if &folder.id == folder_id {
+ folder_name = Some(folder.name.clone());
+ }
}
- }
- (folder_name, Some(folder_id))
- } else {
- (None, None)
- };
+ (folder_name, Some(folder_id))
+ });
let data = if let Some(login) = &self.login {
crate::db::EntryData::Login {
username: login.username.clone(),
@@ -332,7 +334,7 @@ impl SyncResCipher {
} else {
return None;
};
- let fields = if let Some(fields) = &self.fields {
+ let fields = self.fields.as_ref().map_or_else(Vec::new, |fields| {
fields
.iter()
.map(|field| crate::db::Field {
@@ -340,9 +342,7 @@ impl SyncResCipher {
value: field.value.clone(),
})
.collect()
- } else {
- vec![]
- };
+ });
Some(crate::db::Entry {
id: self.id.clone(),
org_id: self.organization_id.clone(),
@@ -554,6 +554,7 @@ pub struct Client {
}
impl Client {
+ #[must_use]
pub fn new(base_url: &str, identity_url: &str) -> Self {
Self {
base_url: base_url.to_string(),
@@ -607,7 +608,7 @@ impl Client {
.send()
.await
.map_err(|source| Error::Reqwest { source })?;
- if let reqwest::StatusCode::OK = res.status() {
+ if res.status() == reqwest::StatusCode::OK {
Ok(())
} else {
let code = res.status().as_u16();
@@ -636,6 +637,9 @@ impl Client {
device_push_token: "".to_string(),
two_factor_token: two_factor_token
.map(std::string::ToString::to_string),
+ // enum casts are safe, and i don't think there's a better way to
+ // write it without some explicit impls
+ #[allow(clippy::as_conversions)]
two_factor_provider: two_factor_provider.map(|ty| ty as u32),
};
let client = reqwest::Client::new();
@@ -649,7 +653,7 @@ impl Client {
.send()
.await
.map_err(|source| Error::Reqwest { source })?;
- if let reqwest::StatusCode::OK = res.status() {
+ if res.status() == reqwest::StatusCode::OK {
let connect_res: ConnectPasswordRes =
res.json_with_path().await?;
Ok((
diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs
index 1cc71c3..87a8276 100644
--- a/src/bin/rbw-agent/actions.rs
+++ b/src/bin/rbw-agent/actions.rs
@@ -61,10 +61,9 @@ pub async fn register(
message,
})
.context("failed to log in to bitwarden instance");
- } else {
- err_msg = Some(message);
- continue;
}
+ err_msg = Some(message);
+ continue;
}
Err(e) => {
return Err(e)
@@ -172,9 +171,8 @@ pub async fn login(
)
.await?;
break;
- } else {
- return Err(anyhow::anyhow!("TODO"));
}
+ return Err(anyhow::anyhow!("TODO"));
}
Err(rbw::error::Error::IncorrectPassword { message }) => {
if i == 3 {
@@ -182,10 +180,9 @@ pub async fn login(
message,
})
.context("failed to log in to bitwarden instance");
- } else {
- err_msg = Some(message);
- continue;
}
+ err_msg = Some(message);
+ continue;
}
Err(e) => {
return Err(e)
@@ -249,10 +246,9 @@ async fn two_factor(
message,
})
.context("failed to log in to bitwarden instance");
- } else {
- err_msg = Some(message);
- continue;
}
+ err_msg = Some(message);
+ continue;
}
// can get this if the user passes an empty string
Err(rbw::error::Error::TwoFactorRequired { .. }) => {
@@ -262,10 +258,9 @@ async fn two_factor(
message,
})
.context("failed to log in to bitwarden instance");
- } else {
- err_msg = Some(message);
- continue;
}
+ err_msg = Some(message);
+ continue;
}
Err(e) => {
return Err(e)
@@ -313,8 +308,7 @@ async fn login_success(
&protected_key,
&protected_private_key,
&db.protected_org_keys,
- )
- .await;
+ );
match res {
Ok((keys, org_keys)) => {
@@ -362,7 +356,7 @@ pub async fn unlock(
let email = config_email().await?;
let mut err_msg = None;
- for i in 1u8..=3 {
+ for i in 1_u8..=3 {
let err = if i > 1 {
// this unwrap is safe because we only ever continue the loop
// if we have set err_msg
@@ -387,9 +381,7 @@ pub async fn unlock(
&protected_key,
&protected_private_key,
&db.protected_org_keys,
- )
- .await
- {
+ ) {
Ok((keys, org_keys)) => {
unlock_success(state, keys, org_keys).await?;
break;
@@ -400,10 +392,9 @@ pub async fn unlock(
message,
})
.context("failed to unlock database");
- } else {
- err_msg = Some(message);
- continue;
}
+ err_msg = Some(message);
+ continue;
}
Err(e) => return Err(e).context("failed to unlock database"),
}
@@ -579,11 +570,10 @@ async fn respond_encrypt(
async fn config_email() -> anyhow::Result<String> {
let config = rbw::config::Config::load_async().await?;
- if let Some(email) = config.email {
- Ok(email)
- } else {
- Err(anyhow::anyhow!("failed to find email address in config"))
- }
+ config.email.map_or_else(
+ || Err(anyhow::anyhow!("failed to find email address in config")),
+ Ok,
+ )
}
async fn load_db() -> anyhow::Result<rbw::db::Db> {
diff --git a/src/bin/rbw-agent/agent.rs b/src/bin/rbw-agent/agent.rs
index fae8c7b..d64bf21 100644
--- a/src/bin/rbw-agent/agent.rs
+++ b/src/bin/rbw-agent/agent.rs
@@ -32,7 +32,7 @@ impl State {
pub fn clear(&mut self) {
self.priv_key = None;
- self.org_keys = Default::default();
+ self.org_keys = None;
// no real better option to unwrap here
self.timeout_chan.send(TimeoutEvent::Clear).unwrap();
}
@@ -57,7 +57,7 @@ impl Agent {
timeout_chan: r,
state: std::sync::Arc::new(tokio::sync::RwLock::new(State {
priv_key: None,
- org_keys: Default::default(),
+ org_keys: None,
timeout_chan: w,
})),
})
@@ -81,11 +81,7 @@ impl Agent {
tokio::time::Duration::from_secs(60 * 60 * 24 * 365 * 2),
));
loop {
- let timeout = if let Some(timeout) = &mut self.timeout {
- timeout
- } else {
- &mut forever
- };
+ let timeout = self.timeout.as_mut().unwrap_or(&mut forever);
tokio::select! {
sock = listener.accept() => {
let mut sock = crate::sock::Sock::new(
diff --git a/src/bin/rbw-agent/main.rs b/src/bin/rbw-agent/main.rs
index 69411ae..f5d478d 100644
--- a/src/bin/rbw-agent/main.rs
+++ b/src/bin/rbw-agent/main.rs
@@ -1,4 +1,15 @@
+#![warn(clippy::cargo)]
+#![warn(clippy::pedantic)]
+#![warn(clippy::nursery)]
+#![warn(clippy::as_conversions)]
+#![warn(clippy::get_unwrap)]
+#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::missing_const_for_fn)]
+#![allow(clippy::similar_names)]
+#![allow(clippy::struct_excessive_bools)]
#![allow(clippy::too_many_arguments)]
+#![allow(clippy::too_many_lines)]
+#![allow(clippy::type_complexity)]
use anyhow::Context as _;
@@ -29,11 +40,9 @@ fn real_main() -> anyhow::Result<()> {
)
.init();
- let no_daemonize = if let Some(arg) = std::env::args().nth(1) {
- arg == "--no-daemonize"
- } else {
- false
- };
+ let no_daemonize = std::env::args()
+ .nth(1)
+ .map_or(false, |arg| arg == "--no-daemonize");
let startup_ack = if no_daemonize {
None
diff --git a/src/bin/rbw-agent/sock.rs b/src/bin/rbw-agent/sock.rs
index 311176c..9458239 100644
--- a/src/bin/rbw-agent/sock.rs
+++ b/src/bin/rbw-agent/sock.rs
@@ -45,6 +45,8 @@ impl Sock {
pub fn listen() -> anyhow::Result<tokio::net::UnixListener> {
let path = rbw::dirs::socket_file();
// if the socket already doesn't exist, that's fine
+ // https://github.com/rust-lang/rust-clippy/issues/8003
+ #[allow(clippy::let_underscore_drop)]
let _ = std::fs::remove_file(&path);
let sock = tokio::net::UnixListener::bind(&path)
.context("failed to listen on socket")?;
diff --git a/src/bin/rbw/actions.rs b/src/bin/rbw/actions.rs
index 39fde15..321bff5 100644
--- a/src/bin/rbw/actions.rs
+++ b/src/bin/rbw/actions.rs
@@ -35,7 +35,7 @@ pub fn quit() -> anyhow::Result<()> {
sock.send(&rbw::protocol::Request {
tty: nix::unistd::ttyname(0)
.ok()
- .and_then(|p| p.to_str().map(|s| s.to_string())),
+ .and_then(|p| p.to_str().map(std::string::ToString::to_string)),
action: rbw::protocol::Action::Quit,
})?;
wait_for_exit(pid);
@@ -59,7 +59,7 @@ pub fn decrypt(
sock.send(&rbw::protocol::Request {
tty: nix::unistd::ttyname(0)
.ok()
- .and_then(|p| p.to_str().map(|s| s.to_string())),
+ .and_then(|p| p.to_str().map(std::string::ToString::to_string)),
action: rbw::protocol::Action::Decrypt {
cipherstring: cipherstring.to_string(),
org_id: org_id.map(std::string::ToString::to_string),
@@ -84,7 +84,7 @@ pub fn encrypt(
sock.send(&rbw::protocol::Request {
tty: nix::unistd::ttyname(0)
.ok()
- .and_then(|p| p.to_str().map(|s| s.to_string())),
+ .and_then(|p| p.to_str().map(std::string::ToString::to_string)),
action: rbw::protocol::Action::Encrypt {
plaintext: plaintext.to_string(),
org_id: org_id.map(std::string::ToString::to_string),
@@ -106,7 +106,7 @@ pub fn version() -> anyhow::Result<u32> {
sock.send(&rbw::protocol::Request {
tty: nix::unistd::ttyname(0)
.ok()
- .and_then(|p| p.to_str().map(|s| s.to_string())),
+ .and_then(|p| p.to_str().map(std::string::ToString::to_string)),
action: rbw::protocol::Action::Version,
})?;
@@ -126,7 +126,7 @@ fn simple_action(action: rbw::protocol::Action) -> anyhow::Result<()> {
sock.send(&rbw::protocol::Request {
tty: nix::unistd::ttyname(0)
.ok()
- .and_then(|p| p.to_str().map(|s| s.to_string())),
+ .and_then(|p| p.to_str().map(std::string::ToString::to_string)),
action,
})?;
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs
index 9efd966..0068efd 100644
--- a/src/bin/rbw/commands.rs
+++ b/src/bin/rbw/commands.rs
@@ -26,22 +26,28 @@ impl DecryptedCipher {
fn display_short(&self, desc: &str) -> bool {
match &self.data {
DecryptedData::Login { password, .. } => {
- if let Some(password) = password {
- println!("{}", password);
- true
- } else {
- eprintln!("entry for '{}' had no password", desc);
- false
- }
+ password.as_ref().map_or_else(
+ || {
+ eprintln!("entry for '{}' had no password", desc);
+ false
+ },
+ |password| {
+ println!("{}", password);
+ true
+ },
+ )
}
DecryptedData::Card { number, .. } => {
- if let Some(number) = number {
- println!("{}", number);
- true
- } else {
- eprintln!("entry for '{}' had no card number", desc);
- false
- }
+ number.as_ref().map_or_else(
+ || {
+ eprintln!("entry for '{}' had no card number", desc);
+ false
+ },
+ |number| {
+ println!("{}", number);
+ true
+ },
+ )
}
DecryptedData::Identity {
title,
@@ -65,15 +71,16 @@ impl DecryptedCipher {
true
}
}
- DecryptedData::SecureNote {} => {
- if let Some(notes) = &self.notes {
- println!("{}", notes);
- true
- } else {
+ DecryptedData::SecureNote {} => self.notes.as_ref().map_or_else(
+ || {
eprintln!("entry for '{}' had no notes", desc);
false
- }
- }
+ },
+ |notes| {
+ println!("{}", notes);
+ true
+ },
+ ),
}
}
@@ -86,18 +93,15 @@ impl DecryptedCipher {
..
} => {
let mut displayed = self.display_short(desc);
- displayed |=
- self.display_field("Username", username.as_deref());
- displayed |=
- self.display_field("TOTP Secret", totp.as_deref());
+ displayed |= display_field("Username", username.as_deref());
+ displayed |= display_field("TOTP Secret", totp.as_deref());
if let Some(uris) = uris {
for uri in uris {
- displayed |=
- self.display_field("URI", Some(&uri.uri));
+ displayed |= display_field("URI", Some(&uri.uri));
let match_type =
uri.match_type.map(|ty| format!("{}", ty));
- displayed |= self.display_field(
+ displayed |= display_field(
"Match type",
match_type.as_deref(),
);
@@ -105,7 +109,7 @@ impl DecryptedCipher {
}
for field in &self.fields {
- displayed |= self.display_field(
+ displayed |= display_field(
field.name.as_deref().unwrap_or("(null)"),
Some(field.value.as_deref().unwrap_or("")),
);
@@ -134,10 +138,10 @@ impl DecryptedCipher {
println!("Expiration: {}/{}", exp_month, exp_year);
displayed = true;
}
- displayed |= self.display_field("CVV", code.as_deref());
+ displayed |= display_field("CVV", code.as_deref());
displayed |=
- self.display_field("Name", cardholder_name.as_deref());
- displayed |= self.display_field("Brand", brand.as_deref());
+ display_field("Name", cardholder_name.as_deref());
+ displayed |= display_field("Brand", brand.as_deref());
if let Some(notes) = &self.notes {
if displayed {
@@ -164,27 +168,22 @@ impl DecryptedCipher {
} => {
let mut displayed = self.display_short(desc);
+ displayed |= display_field("Address", address1.as_deref());
+ displayed |= display_field("Address", address2.as_deref());
+ displayed |= display_field("Address", address3.as_deref());
+ displayed |= display_field("City", city.as_deref());
+ displayed |= display_field("State", state.as_deref());
displayed |=
- self.display_field("Address", address1.as_deref());
- displayed |=
- self.display_field("Address", address2.as_deref());
- displayed |=
- self.display_field("Address", address3.as_deref());
- displayed |= self.display_field("City", city.as_deref());
- displayed |= self.display_field("State", state.as_deref());
- displayed |=
- self.display_field("Postcode", postal_code.as_deref());
- displayed |=
- self.display_field("Country", country.as_deref());
- displayed |= self.display_field("Phone", phone.as_deref());
- displayed |= self.display_field("Email", email.as_deref());
- displayed |= self.display_field("SSN", ssn.as_deref());
+ display_field("Postcode", postal_code.as_deref());
+ displayed |= display_field("Country", country.as_deref());
+ displayed |= display_field("Phone", phone.as_deref());
+ displayed |= display_field("Email", email.as_deref());
+ displayed |= display_field("SSN", ssn.as_deref());
displayed |=
- self.display_field("License", license_number.as_deref());
- displayed |= self
- .display_field("Passport", passport_number.as_deref());
+ display_field("License", license_number.as_deref());
displayed |=
- self.display_field("Username", username.as_deref());
+ display_field("Passport", passport_number.as_deref());
+ displayed |= display_field("Username", username.as_deref());
if let Some(notes) = &self.notes {
if displayed {
@@ -199,23 +198,13 @@ impl DecryptedCipher {
}
}
- fn display_field(&self, name: &str, field: Option<&str>) -> bool {
- if let Some(field) = field {
- println!("{}: {}", name, field);
- true
- } else {
- false
- }
- }
-
fn display_name(&self) -> String {
match &self.data {
DecryptedData::Login { username, .. } => {
- if let Some(username) = username {
- format!("{}@{}", username, self.name)
- } else {
- self.name.clone()
- }
+ username.as_ref().map_or_else(
+ || self.name.clone(),
+ |username| format!("{}@{}", username, self.name),
+ )
}
_ => self.name.clone(),
}
@@ -314,6 +303,7 @@ impl DecryptedCipher {
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
+#[allow(clippy::large_enum_variant)]
enum DecryptedData {
Login {
username: Option<String>,
@@ -448,7 +438,7 @@ pub fn config_unset(key: &str) -> anyhow::Result<()> {
"base_url" => config.base_url = None,
"identity_url" => config.identity_url = None,
"lock_timeout" => {
- config.lock_timeout = rbw::config::default_lock_timeout()
+ config.lock_timeout = rbw::config::default_lock_timeout();
}
"pinentry" => config.pinentry = rbw::config::default_pinentry(),
_ => return Err(anyhow::anyhow!("invalid config key: {}", key)),
@@ -526,17 +516,18 @@ pub fn list(fields: &[String]) -> anyhow::Result<()> {
ListField::Name => cipher.name.clone(),
ListField::Id => cipher.id.clone(),
ListField::User => match &cipher.data {
- DecryptedData::Login { username, .. } => username
- .as_ref()
- .map(std::string::ToString::to_string)
- .unwrap_or_else(|| "".to_string()),
+ DecryptedData::Login { username, .. } => {
+ username.as_ref().map_or_else(
+ || "".to_string(),
+ std::string::ToString::to_string,
+ )
+ }
_ => "".to_string(),
},
- ListField::Folder => cipher
- .folder
- .as_ref()
- .map(std::string::ToString::to_string)
- .unwrap_or_else(|| "".to_string()),
+ ListField::Folder => cipher.folder.as_ref().map_or_else(
+ || "".to_string(),
+ std::string::ToString::to_string,
+ ),
})
.collect();
println!("{}", values.join("\t"));
@@ -557,8 +548,7 @@ pub fn get(
let desc = format!(
"{}{}",
- user.map(|s| format!("{}@", s))
- .unwrap_or_else(|| "".to_string()),
+ user.map_or_else(|| "".to_string(), |s| format!("{}@", s)),
name
);
@@ -584,8 +574,7 @@ pub fn code(
let desc = format!(
"{}{}",
- user.map(|s| format!("{}@", s))
- .unwrap_or_else(|| "".to_string()),
+ user.map_or_else(|| "".to_string(), |s| format!("{}@", s)),
name
);
@@ -594,7 +583,7 @@ pub fn code(
if let DecryptedData::Login { totp, .. } = decrypted.data {
if let Some(totp) = totp {
- println!("{}", generate_totp(&totp)?)
+ println!("{}", generate_totp(&totp)?);
} else {
return Err(anyhow::anyhow!(
"entry does not contain a totp secret"
@@ -610,7 +599,7 @@ pub fn code(
pub fn add(
name: &str,
username: Option<&str>,
- uris: Vec<(String, Option<rbw::api::UriMatchType>)>,
+ uris: &[(String, Option<rbw::api::UriMatchType>)],
folder: Option<&str>,
) -> anyhow::Result<()> {
unlock()?;
@@ -707,7 +696,7 @@ pub fn add(
pub fn generate(
name: Option<&str>,
username: Option<&str>,
- uris: Vec<(String, Option<rbw::api::UriMatchType>)>,
+ uris: &[(String, Option<rbw::api::UriMatchType>)],
folder: Option<&str>,
len: usize,
ty: rbw::pwgen::Type,
@@ -813,9 +802,7 @@ pub fn edit(
let desc = format!(
"{}{}",
- username
- .map(|s| format!("{}@", s))
- .unwrap_or_else(|| "".to_string()),
+ username.map_or_else(|| "".to_string(), |s| format!("{}@", s)),
name
);
@@ -874,7 +861,7 @@ pub fn edit(
let data = rbw::db::EntryData::Login {
username: entry_username.clone(),
password,
- uris: entry_uris.to_vec(),
+ uris: entry_uris.clone(),
totp: entry_totp.clone(),
};
(data, notes, history)
@@ -918,9 +905,7 @@ pub fn remove(
let desc = format!(
"{}{}",
- username
- .map(|s| format!("{}@", s))
- .unwrap_or_else(|| "".to_string()),
+ username.map_or_else(|| "".to_string(), |s| format!("{}@", s)),
name
);
@@ -950,9 +935,7 @@ pub fn history(
let desc = format!(
"{}{}",
- username
- .map(|s| format!("{}@", s))
- .unwrap_or_else(|| "".to_string()),
+ username.map_or_else(|| "".to_string(), |s| format!("{}@", s)),
name
);
@@ -1017,7 +1000,7 @@ fn ensure_agent_once() -> anyhow::Result<()> {
let agent_path = std::env::var("RBW_AGENT");
let agent_path = agent_path
.as_ref()
- .map(|s| s.as_str())
+ .map(std::string::String::as_str)
.unwrap_or("rbw-agent");
let status = std::process::Command::new(agent_path)
.status()
@@ -1045,6 +1028,8 @@ fn check_config() -> anyhow::Result<()> {
fn version_or_quit() -> anyhow::Result<u32> {
crate::actions::version().map_err(|e| {
+ // https://github.com/rust-lang/rust-clippy/issues/8003
+ #[allow(clippy::let_underscore_drop)]
let _ = crate::actions::quit();
e
})
@@ -1056,26 +1041,23 @@ fn find_entry(
username: Option<&str>,
folder: Option<&str>,
) -> anyhow::Result<(rbw::db::Entry, DecryptedCipher)> {
- match uuid::Uuid::parse_str(name) {
- Ok(_) => {
- for cipher in &db.entries {
- if name == cipher.id {
- return Ok((cipher.clone(), decrypt_cipher(cipher)?));
- }
+ if uuid::Uuid::parse_str(name).is_ok() {
+ for cipher in &db.entries {
+ if name == cipher.id {
+ return Ok((cipher.clone(), decrypt_cipher(cipher)?));
}
- Err(anyhow::anyhow!("no entry found"))
- }
- Err(_) => {
- let ciphers: Vec<(rbw::db::Entry, DecryptedCipher)> = db
- .entries
- .iter()
- .cloned()
- .map(|entry| {
- decrypt_cipher(&entry).map(|decrypted| (entry, decrypted))
- })
- .collect::<anyhow::Result<_>>()?;
- find_entry_raw(&ciphers, name, username, folder)
}
+ Err(anyhow::anyhow!("no entry found"))
+ } else {
+ let ciphers: Vec<(rbw::db::Entry, DecryptedCipher)> = db
+ .entries
+ .iter()
+ .cloned()
+ .map(|entry| {
+ decrypt_cipher(&entry).map(|decrypted| (entry, decrypted))
+ })
+ .collect::<anyhow::Result<_>>()?;
+ find_entry_raw(&ciphers, name, username, folder)
}
}
@@ -1447,32 +1429,35 @@ fn parse_editor(contents: &str) -> (Option<String>, Option<String>) {
fn load_db() -> anyhow::Result<rbw::db::Db> {
let config = rbw::config::Config::load()?;
- if let Some(email) = &config.email {
- rbw::db::Db::load(&config.server_name(), email)
- .map_err(anyhow::Error::new)
- } else {
- Err(anyhow::anyhow!("failed to find email address in config"))
- }
+ config.email.as_ref().map_or_else(
+ || Err(anyhow::anyhow!("failed to find email address in config")),
+ |email| {
+ rbw::db::Db::load(&config.server_name(), email)
+ .map_err(anyhow::Error::new)
+ },
+ )
}
fn save_db(db: &rbw::db::Db) -> anyhow::Result<()> {
let config = rbw::config::Config::load()?;
- if let Some(email) = &config.email {
- db.save(&config.server_name(), email)
- .map_err(anyhow::Error::new)
- } else {
- Err(anyhow::anyhow!("failed to find email address in config"))
- }
+ config.email.as_ref().map_or_else(
+ || Err(anyhow::anyhow!("failed to find email address in config")),
+ |email| {
+ db.save(&config.server_name(), email)
+ .map_err(anyhow::Error::new)
+ },
+ )
}
fn remove_db() -> anyhow::Result<()> {
let config = rbw::config::Config::load()?;
- if let Some(email) = &config.email {
- rbw::db::Db::remove(&config.server_name(), email)
- .map_err(anyhow::Error::new)
- } else {
- Err(anyhow::anyhow!("failed to find email address in config"))
- }
+ config.email.as_ref().map_or_else(
+ || Err(anyhow::anyhow!("failed to find email address in config")),
+ |email| {
+ rbw::db::Db::remove(&config.server_name(), email)
+ .map_err(anyhow::Error::new)
+ },
+ )
}
fn parse_totp_secret(secret: &str) -> anyhow::Result<Vec<u8>> {
@@ -1680,3 +1665,13 @@ mod test {
)
}
}
+
+fn display_field(name: &str, field: Option<&str>) -> bool {
+ field.map_or_else(
+ || false,
+ |field| {
+ println!("{}: {}", name, field);
+ true
+ },
+ )
+}
diff --git a/src/bin/rbw/main.rs b/src/bin/rbw/main.rs
index 85631c5..5730298 100644
--- a/src/bin/rbw/main.rs
+++ b/src/bin/rbw/main.rs
@@ -1,4 +1,15 @@
-#![allow(clippy::large_enum_variant)]
+#![warn(clippy::cargo)]
+#![warn(clippy::pedantic)]
+#![warn(clippy::nursery)]
+#![warn(clippy::as_conversions)]
+#![warn(clippy::get_unwrap)]
+#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::missing_const_for_fn)]
+#![allow(clippy::similar_names)]
+#![allow(clippy::struct_excessive_bools)]
+#![allow(clippy::too_many_arguments)]
+#![allow(clippy::too_many_lines)]
+#![allow(clippy::type_complexity)]
use anyhow::Context as _;
use std::io::Write as _;
@@ -319,7 +330,7 @@ fn main(opt: Opt) {
} => commands::add(
name,
user.as_deref(),
- uri.iter()
+ &uri.iter()
// XXX not sure what the ui for specifying the match type
// should be
.map(|uri| (uri.clone(), None))
@@ -351,7 +362,7 @@ fn main(opt: Opt) {
commands::generate(
name.as_deref(),
user.as_deref(),
- uri.iter()
+ &uri.iter()
// XXX not sure what the ui for specifying the match type
// should be
.map(|uri| (uri.clone(), None))
diff --git a/src/config.rs b/src/config.rs
index bbc39f7..9b47590 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -19,9 +19,9 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
- email: Default::default(),
- base_url: Default::default(),
- identity_url: Default::default(),
+ email: None,
+ base_url: None,
+ identity_url: None,
lock_timeout: default_lock_timeout(),
pinentry: default_pinentry(),
device_id: default_device_id(),
@@ -29,23 +29,28 @@ impl Default for Config {
}
}
+#[must_use]
pub fn default_lock_timeout() -> u64 {
3600
}
+#[must_use]
pub fn default_pinentry() -> String {
"pinentry".to_string()
}
+#[must_use]
fn default_device_id() -> String {
uuid::Uuid::new_v4().to_hyphenated().to_string()
}
+#[must_use]
fn stub_device_id() -> String {
String::from("fix")
}
impl Config {
+ #[must_use]
pub fn new() -> Self {
Self::default()
}
@@ -138,6 +143,7 @@ impl Config {
Ok(())
}
+ #[must_use]
pub fn base_url(&self) -> String {
self.base_url.clone().map_or_else(
|| "https://api.bitwarden.com".to_string(),
@@ -145,6 +151,7 @@ impl Config {
)
}
+ #[must_use]
pub fn identity_url(&self) -> String {
self.identity_url.clone().unwrap_or_else(|| {
self.base_url.clone().map_or_else(
@@ -154,6 +161,7 @@ impl Config {
})
}
+ #[must_use]
pub fn server_name(&self) -> String {
self.base_url
.clone()
diff --git a/src/db.rs b/src/db.rs
index 5359321..cc9319e 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -106,6 +106,7 @@ impl<'de> serde::Deserialize<'de> for Uri {
#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
)]
+#[allow(clippy::large_enum_variant)]
pub enum EntryData {
Login {
username: Option<String>,
@@ -173,6 +174,7 @@ pub struct Db {
}
impl Db {
+ #[must_use]
pub fn new() -> Self {
Self::default()
}
@@ -287,6 +289,7 @@ impl Db {
Ok(())
}
+ #[must_use]
pub fn needs_login(&self) -> bool {
self.access_token.is_none()
|| self.refresh_token.is_none()
diff --git a/src/dirs.rs b/src/dirs.rs
index 285a0d5..bc97ed5 100644
--- a/src/dirs.rs
+++ b/src/dirs.rs
@@ -37,12 +37,14 @@ pub fn make_all() -> Result<()> {
Ok(())
}
+#[must_use]
pub fn config_file() -> std::path::PathBuf {
config_dir().join("config.json")
}
const INVALID_PATH: &percent_encoding::AsciiSet =
&percent_encoding::CONTROLS.add(b'/').add(b'%').add(b':');
+#[must_use]
pub fn db_file(server: &str, email: &str) -> std::path::PathBuf {
let server =
percent_encoding::percent_encode(server.as_bytes(), INVALID_PATH)
@@ -50,37 +52,45 @@ pub fn db_file(server: &str, email: &str) -> std::path::PathBuf {
cache_dir().join(format!("{}:{}.json", server, email))
}
+#[must_use]
pub fn pid_file() -> std::path::PathBuf {
runtime_dir().join("pidfile")
}
+#[must_use]
pub fn agent_stdout_file() -> std::path::PathBuf {
data_dir().join("agent.out")
}
+#[must_use]
pub fn agent_stderr_file() -> std::path::PathBuf {
data_dir().join("agent.err")
}
+#[must_use]
pub fn socket_file() -> std::path::PathBuf {
runtime_dir().join("socket")
}
+#[must_use]
fn config_dir() -> std::path::PathBuf {
let project_dirs = directories::ProjectDirs::from("", "", "rbw").unwrap();
project_dirs.config_dir().to_path_buf()
}
+#[must_use]
fn cache_dir() -> std::path::PathBuf {
let project_dirs = directories::ProjectDirs::from("", "", "rbw").unwrap();
project_dirs.cache_dir().to_path_buf()
}
+#[must_use]
fn data_dir() -> std::path::PathBuf {
let project_dirs = directories::ProjectDirs::from("", "", "rbw").unwrap();
project_dirs.data_dir().to_path_buf()
}
+#[must_use]
fn runtime_dir() -> std::path::PathBuf {
let project_dirs = directories::ProjectDirs::from("", "", "rbw").unwrap();
match project_dirs.runtime_dir() {
diff --git a/src/edit.rs b/src/edit.rs
index 1a831a7..8f4e534 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -30,6 +30,7 @@ pub fn edit(contents: &str, help: &str) -> Result<String> {
let editor = std::path::Path::new(&editor);
let mut editor_args = vec![];
+ #[allow(clippy::single_match)] // more to come
match editor.file_name() {
Some(editor) => match editor.to_str() {
Some("vim" | "nvim") => {
diff --git a/src/lib.rs b/src/lib.rs
index 4a13e25..db615c0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,18 +1,18 @@
+#![warn(clippy::cargo)]
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
-#![allow(clippy::default_trait_access)]
-#![allow(clippy::implicit_hasher)]
-#![allow(clippy::large_enum_variant)]
+#![warn(clippy::as_conversions)]
+#![warn(clippy::get_unwrap)]
+#![allow(clippy::cognitive_complexity)]
#![allow(clippy::missing_const_for_fn)]
-#![allow(clippy::missing_errors_doc)]
-#![allow(clippy::missing_panics_doc)]
-#![allow(clippy::must_use_candidate)]
#![allow(clippy::similar_names)]
-#![allow(clippy::single_match)]
+#![allow(clippy::struct_excessive_bools)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::type_complexity)]
-#![allow(clippy::unused_async)]
+// we aren't really documenting apis anyway
+#![allow(clippy::missing_errors_doc)]
+#![allow(clippy::missing_panics_doc)]
pub mod actions;
pub mod api;
diff --git a/src/locked.rs b/src/locked.rs
index 4ddf021..bfa642d 100644
--- a/src/locked.rs
+++ b/src/locked.rs
@@ -18,10 +18,12 @@ impl Default for Vec {
}
impl Vec {
+ #[must_use]
pub fn new() -> Self {
Self::default()
}
+ #[must_use]
pub fn data(&self) -> &[u8] {
self.data.as_slice()
}
@@ -65,10 +67,12 @@ pub struct Password {
}
impl Password {
+ #[must_use]
pub fn new(password: Vec) -> Self {
Self { password }
}
+ #[must_use]
pub fn password(&self) -> &[u8] {
self.password.data()
}
@@ -80,14 +84,17 @@ pub struct Keys {
}
impl Keys {
+ #[must_use]
pub fn new(keys: Vec) -> Self {
Self { keys }
}
+ #[must_use]
pub fn enc_key(&self) -> &[u8] {
&self.keys.data()[0..32]
}
+ #[must_use]
pub fn mac_key(&self) -> &[u8] {
&self.keys.data()[32..64]
}
@@ -99,10 +106,12 @@ pub struct PasswordHash {
}
impl PasswordHash {
+ #[must_use]
pub fn new(hash: Vec) -> Self {
Self { hash }
}
+ #[must_use]
pub fn hash(&self) -> &[u8] {
self.hash.data()
}
@@ -114,10 +123,12 @@ pub struct PrivateKey {
}
impl PrivateKey {
+ #[must_use]
pub fn new(private_key: Vec) -> Self {
Self { private_key }
}
+ #[must_use]
pub fn private_key(&self) -> &[u8] {
self.private_key.data()
}
@@ -130,6 +141,7 @@ pub struct ApiKey {
}
impl ApiKey {
+ #[must_use]
pub fn new(client_id: Password, client_secret: Password) -> Self {
Self {
client_id,
@@ -137,10 +149,12 @@ impl ApiKey {
}
}
+ #[must_use]
pub fn client_id(&self) -> &[u8] {
self.client_id.password()
}
+ #[must_use]
pub fn client_secret(&self) -> &[u8] {
self.client_secret.password()
}
diff --git a/src/pinentry.rs b/src/pinentry.rs
index b4d2bb0..ce1227f 100644
--- a/src/pinentry.rs
+++ b/src/pinentry.rs
@@ -1,5 +1,6 @@
use crate::prelude::*;
+use std::convert::TryFrom as _;
use tokio::io::AsyncWriteExt as _;
pub async fn getpin(
@@ -163,7 +164,10 @@ fn percent_decode(buf: &mut [u8]) -> usize {
if let Some(h) = char::from(buf[read_idx + 1]).to_digit(16) {
#[allow(clippy::cast_possible_truncation)]
if let Some(l) = char::from(buf[read_idx + 2]).to_digit(16) {
- c = h as u8 * 0x10 + l as u8;
+ // h and l were parsed from a single hex digit, so they
+ // must be in the range 0-15, so these unwraps are safe
+ c = u8::try_from(h).unwrap() * 0x10
+ + u8::try_from(l).unwrap();
read_idx += 2;
}
}
diff --git a/src/protocol.rs b/src/protocol.rs
index 14fa7f9..a10d301 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -3,6 +3,7 @@
// eventually it would be nice to make this a const function so that we could
// just get the version from a variable directly, but this is fine for now
+#[must_use]
pub fn version() -> u32 {
let major = env!("CARGO_PKG_VERSION_MAJOR");
let minor = env!("CARGO_PKG_VERSION_MINOR");
diff --git a/src/pwgen.rs b/src/pwgen.rs
index 55151e6..a112d73 100644
--- a/src/pwgen.rs
+++ b/src/pwgen.rs
@@ -15,6 +15,7 @@ pub enum Type {
Diceware,
}
+#[must_use]
pub fn pwgen(ty: Type, len: usize) -> String {
let mut rng = rand::thread_rng();
@@ -101,6 +102,6 @@ mod test {
for c in s.chars() {
set.insert(c);
}
- assert!(set.len() < s.len())
+ assert!(set.len() < s.len());
}
}