Clean up type conversion in API impl
Diff
onep-api-op/src/lib.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
onep-api/src/lib.rs | 3 +++
onep-cli/src/main.rs | 18 ++++++++++++------
3 files changed, 102 insertions(+), 68 deletions(-)
@@ -1,3 +1,5 @@
#![deny(clippy::pedantic)]
use serde::Deserialize;
use serde_json::Value;
use std::process::Command;
@@ -11,19 +13,37 @@
#[error("failed to parse json from op:\n{0}")]
Json(#[from] serde_json::error::Error),
#[error("failed to convert op response to utf-8:\n{0}")]
Utf8(#[from] std::str::Utf8Error)
Utf8(#[from] std::str::Utf8Error),
}
#[derive(Debug, Deserialize)]
struct GetAccount {
name: String,
domain: String,
}
impl Into<onep_api::AccountMetadata> for GetAccount {
fn into(self) -> onep_api::AccountMetadata {
onep_api::AccountMetadata {
name: self.name,
domain: self.domain,
}
}
}
#[derive(Debug, Deserialize)]
struct ListVault {
uuid: String,
name: String,
}
impl Into<onep_api::VaultMetadata> for ListVault {
fn into(self) -> onep_api::VaultMetadata {
onep_api::VaultMetadata {
uuid: self.uuid,
name: self.name,
}
}
}
#[derive(Debug, Deserialize)]
@@ -34,6 +54,17 @@
created_at: String,
updated_at: String,
overview: ItemOverview,
}
impl Into<onep_api::ItemMetadata> for ListItem {
fn into(self) -> onep_api::ItemMetadata {
onep_api::ItemMetadata {
title: self.overview.title,
account_info: self.overview.account_info,
uuid: self.uuid,
vault_uuid: self.vault_uuid,
}
}
}
#[derive(Debug, Deserialize)]
@@ -61,6 +92,35 @@
struct GetItem {
details: GetItemDetails,
overview: ItemOverview,
}
impl Into<onep_api::Item> for GetItem {
fn into(self) -> onep_api::Item {
onep_api::Item {
title: self.overview.title,
fields: self
.details
.fields
.into_iter()
.map(|f| f.into())
.filter(|f: &onep_api::ItemField| !f.value.is_empty())
.collect(),
sections: self
.details
.sections
.into_iter()
.map(|v| onep_api::ItemSection {
name: v.title,
fields: v
.fields
.into_iter()
.map(|f| f.into())
.filter(|f: &onep_api::ItemField| !f.value.is_empty())
.collect(),
})
.collect(),
}
}
}
#[derive(Debug, Deserialize)]
@@ -130,17 +190,18 @@
pub struct OnepasswordOp {}
impl OnepasswordOp {
fn exec(&self, args: &[&str]) -> Result<Vec<u8>, Error> {
let cmd = Command::new("op").args(args).output().map_err(Error::Exec)?;
if cmd.status.success() {
Ok(cmd.stdout)
} else {
Err(Error::Backend(
std::str::from_utf8(&cmd.stderr)?.to_string()
))
}
fn exec(args: &[&str]) -> Result<Vec<u8>, Error> {
let cmd = Command::new("op")
.args(args)
.output()
.map_err(Error::Exec)?;
if cmd.status.success() {
Ok(cmd.stdout)
} else {
Err(Error::Backend(
std::str::from_utf8(&cmd.stderr)?.to_string(),
))
}
}
@@ -148,33 +209,27 @@
type Error = Error;
fn totp(&self, uuid: &str) -> Result<String, Self::Error> {
Ok(std::str::from_utf8(&self.exec(&["get", "totp", uuid])?)?.to_string())
Ok(std::str::from_utf8(&exec(&["get", "totp", uuid])?)?.to_string())
}
fn account(&self) -> Result<onep_api::AccountMetadata, Self::Error> {
let ret: GetAccount = serde_json::from_slice(&self.exec(&["get", "account"])?)?;
Ok(onep_api::AccountMetadata {
name: ret.name,
domain: ret.domain,
})
let ret: GetAccount = serde_json::from_slice(&exec(&["get", "account"])?)?;
Ok(ret.into())
}
fn vaults(&self) -> Result<Vec<onep_api::VaultMetadata>, Self::Error> {
let ret: Vec<ListVault> = serde_json::from_slice(&self.exec(&["list", "vaults"])?)?;
Ok(ret.into_iter()
.map(|v| onep_api::VaultMetadata {
uuid: v.uuid,
name: v.name,
})
.collect())
let ret: Vec<ListVault> = serde_json::from_slice(&exec(&["list", "vaults"])?)?;
Ok(ret.into_iter().map(|v| v.into()).collect())
}
#[allow(clippy::filter_map)]
fn search(&self, terms: Option<&str>) -> Result<Vec<onep_api::ItemMetadata>, Self::Error> {
let ret: Vec<ListItem> = serde_json::from_slice(&self.exec(&["list", "items"])?)?;
let ret: Vec<ListItem> = serde_json::from_slice(&exec(&["list", "items"])?)?;
Ok(ret.into_iter()
Ok(ret
.into_iter()
.filter(|v| {
if let Some(terms) = terms {
v.uuid == terms
@@ -186,42 +241,14 @@
} else {
true
}
})
.map(|v| onep_api::ItemMetadata {
title: v.overview.title,
account_info: v.overview.account_info,
uuid: v.uuid,
vault_uuid: v.vault_uuid,
})
.map(|v| v.into())
.collect())
}
fn get(&self, uuid: &str) -> Result<Option<onep_api::Item>, Self::Error> {
let ret: GetItem = serde_json::from_slice(&self.exec(&["get", "item", uuid])?)?;
Ok(Some(onep_api::Item {
title: ret.overview.title,
fields: ret
.details
.fields
.into_iter()
.map(|f| f.into())
.filter(|f: &onep_api::ItemField| !f.value.is_empty())
.collect(),
sections: ret
.details
.sections
.into_iter()
.map(|v| onep_api::ItemSection {
name: v.title,
fields: v
.fields
.into_iter()
.map(|f| f.into())
.filter(|f: &onep_api::ItemField| !f.value.is_empty())
.collect(),
})
.collect(),
}))
let ret: GetItem = serde_json::from_slice(&exec(&["get", "item", uuid])?)?;
Ok(Some(ret.into()))
}
}
@@ -1,3 +1,6 @@
#![deny(clippy::pedantic)]
#![allow(clippy::missing_errors_doc)]
#[derive(Debug)]
pub struct AccountMetadata {
pub name: String,
@@ -1,5 +1,7 @@
#![deny(clippy::pedantic)]
use clap::Clap;
use colored::*;
use colored::Colorize;
use itertools::Itertools;
use onep_api::OnePassword;
use term_table::{
@@ -36,15 +38,18 @@
}
fn main() {
if let Err(e) = run() {
if let Err(e) = run(&onep_api_op::OnepasswordOp {}) {
eprintln!("{}", e);
std::process::exit(1);
}
}
fn run() -> anyhow::Result<()> {
let imp = onep_api_op::OnepasswordOp {};
#[warn(clippy::too_many_lines)]
#[allow(clippy::non_ascii_literal)]
fn run<T: OnePassword>(imp: &T) -> anyhow::Result<()>
where
T::Error: 'static + std::error::Error + Send + Sync,
{
match Opt::parse() {
Opt::Ls {
show_uuids,
@@ -67,8 +72,7 @@
let vault = vaults
.iter()
.find(|v| v.uuid == vault)
.map(|v| v.name.clone())
.unwrap_or_else(|| format!("Unknown Vault ({})", vault));
.map_or_else(|| format!("Unknown Vault ({})", vault), |v| v.name.clone());
println!(
"{} {}",