use std::collections::HashMap;
pub use error::{Error, Result};
pub use search::{CredentialSearch, CredentialSearchResult, Limit};
pub mod mock;
#[cfg(all(target_os = "linux", feature = "linux-keyutils"))]
pub mod keyutils;
#[cfg(all(
target_os = "linux",
feature = "secret-service",
not(feature = "linux-no-secret-service")
))]
pub mod secret_service;
#[cfg(all(
target_os = "linux",
feature = "secret-service",
not(feature = "linux-default-keyutils")
))]
use crate::secret_service as default;
#[cfg(all(
target_os = "linux",
feature = "linux-keyutils",
any(feature = "linux-default-keyutils", not(feature = "secret-service"))
))]
use keyutils as default;
#[cfg(all(
target_os = "linux",
not(feature = "secret-service"),
not(feature = "linux-keyutils")
))]
use mock as default;
#[cfg(all(target_os = "freebsd", feature = "secret-service"))]
pub mod secret_service;
#[cfg(all(target_os = "freebsd", feature = "secret-service"))]
use crate::secret_service as default;
#[cfg(all(target_os = "freebsd", not(feature = "secret-service")))]
use mock as default;
#[cfg(all(target_os = "openbsd", feature = "secret-service"))]
pub mod secret_service;
#[cfg(all(target_os = "openbsd", feature = "secret-service"))]
use crate::secret_service as default;
#[cfg(all(target_os = "openbsd", not(feature = "secret-service")))]
use mock as default;
#[cfg(all(target_os = "macos", feature = "platform-macos"))]
pub mod macos;
#[cfg(all(target_os = "macos", feature = "platform-macos"))]
use macos as default;
#[cfg(all(target_os = "macos", not(feature = "platform-macos")))]
use mock as default;
#[cfg(all(target_os = "windows", feature = "platform-windows"))]
pub mod windows;
#[cfg(all(target_os = "windows", not(feature = "platform-windows")))]
use mock as default;
#[cfg(all(target_os = "windows", feature = "platform-windows"))]
use windows as default;
#[cfg(all(target_os = "ios", feature = "platform-ios"))]
pub mod ios;
#[cfg(all(target_os = "ios", feature = "platform-ios"))]
use ios as default;
#[cfg(all(target_os = "ios", not(feature = "platform-ios")))]
use mock as default;
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "ios",
target_os = "windows",
)))]
use mock as default;
pub mod error;
pub mod search;
pub fn set_default_credential_search(default_search: Box<CredentialSearch>) -> Result<Search> {
Ok(Search {
inner: default_search,
})
}
fn default_credential_search() -> Result<Search> {
let credentials = default::default_credential_search();
Ok(Search { inner: credentials })
}
pub struct Search {
inner: Box<CredentialSearch>,
}
impl Search {
pub fn new() -> Result<Search> {
default_credential_search()
}
pub fn by_target(&self, query: &str) -> CredentialSearchResult {
self.inner.by("target", query)
}
pub fn by_user(&self, query: &str) -> CredentialSearchResult {
self.inner.by("user", query)
}
pub fn by_service(&self, query: &str) -> CredentialSearchResult {
self.inner.by("service", query)
}
}
pub struct List {}
impl List {
pub fn list_credentials(search_result: &CredentialSearchResult, limit: Limit) -> String {
match limit {
Limit::All => Self::list_all(search_result),
Limit::Max(max) => Self::list_max(search_result, max),
}
}
fn list_all(result: &CredentialSearchResult) -> String {
let mut output = String::new();
match result {
Ok(search_result) => {
let mut entries: Vec<(String, HashMap<String, String>)> = search_result
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
entries.sort_by_key(|(k, _)| k.parse::<i32>().unwrap_or(0));
for (outer_key, inner_map) in entries {
output.push_str(&format!("{}\n", outer_key));
let mut metadata: Vec<(String, String)> = inner_map
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
metadata.sort_by(|a, b| a.0.cmp(&b.0));
for (key, value) in metadata {
output.push_str(&format!("{}: {}\n", key, value));
}
}
output
}
Err(err) => err.to_string(),
}
}
fn list_max(result: &CredentialSearchResult, max: i64) -> String {
let mut output = String::new();
let mut count = 1;
match result {
Ok(search_result) => {
let mut entries: Vec<(String, HashMap<String, String>)> = search_result
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
entries.sort_by_key(|(k, _)| k.parse::<i32>().unwrap_or(0));
for (outer_key, inner_map) in entries {
output.push_str(&format!("{}\n", outer_key));
let mut metadata: Vec<(String, String)> = inner_map
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
metadata.sort_by(|a, b| a.0.cmp(&b.0));
for (key, value) in metadata {
output.push_str(&format!("{}: {}\n", key, value));
}
count += 1;
if count > max {
break;
}
}
println!("Search returned {} results\n", search_result.keys().len());
output
}
Err(err) => err.to_string(),
}
}
}
#[cfg(test)]
mod tests {
pub fn generate_random_string_of_len(len: usize) -> String {
use rand::{distributions::Alphanumeric, thread_rng, Rng};
thread_rng()
.sample_iter(&Alphanumeric)
.take(len)
.map(char::from)
.collect()
}
pub fn generate_random_string() -> String {
generate_random_string_of_len(30)
}
}