1use std::collections::HashMap;
20
21pub use error::{Error, Result};
22pub use search::{CredentialSearch, CredentialSearchResult, Limit};
23pub mod mock;
26
27#[cfg(all(target_os = "linux", feature = "linux-keyutils"))]
28pub mod keyutils;
29#[cfg(all(
30 target_os = "linux",
31 feature = "secret-service",
32 not(feature = "linux-no-secret-service")
33))]
34pub mod secret_service;
35#[cfg(all(
36 target_os = "linux",
37 feature = "secret-service",
38 not(feature = "linux-default-keyutils")
39))]
40use crate::secret_service as default;
41#[cfg(all(
42 target_os = "linux",
43 feature = "linux-keyutils",
44 any(feature = "linux-default-keyutils", not(feature = "secret-service"))
45))]
46use keyutils as default;
47#[cfg(all(
48 target_os = "linux",
49 not(feature = "secret-service"),
50 not(feature = "linux-keyutils")
51))]
52use mock as default;
53
54#[cfg(all(target_os = "freebsd", feature = "secret-service"))]
55pub mod secret_service;
56#[cfg(all(target_os = "freebsd", feature = "secret-service"))]
57use crate::secret_service as default;
58#[cfg(all(target_os = "freebsd", not(feature = "secret-service")))]
59use mock as default;
60
61#[cfg(all(target_os = "openbsd", feature = "secret-service"))]
62pub mod secret_service;
63#[cfg(all(target_os = "openbsd", feature = "secret-service"))]
64use crate::secret_service as default;
65#[cfg(all(target_os = "openbsd", not(feature = "secret-service")))]
66use mock as default;
67
68#[cfg(all(target_os = "macos", feature = "platform-macos"))]
69pub mod macos;
70#[cfg(all(target_os = "macos", feature = "platform-macos"))]
71use macos as default;
72#[cfg(all(target_os = "macos", not(feature = "platform-macos")))]
73use mock as default;
74
75#[cfg(all(target_os = "windows", feature = "platform-windows"))]
76pub mod windows;
77#[cfg(all(target_os = "windows", not(feature = "platform-windows")))]
78use mock as default;
79#[cfg(all(target_os = "windows", feature = "platform-windows"))]
80use windows as default;
81
82#[cfg(all(target_os = "ios", feature = "platform-ios"))]
83pub mod ios;
84#[cfg(all(target_os = "ios", feature = "platform-ios"))]
85use ios as default;
86#[cfg(all(target_os = "ios", not(feature = "platform-ios")))]
87use mock as default;
88
89#[cfg(not(any(
90 target_os = "linux",
91 target_os = "freebsd",
92 target_os = "openbsd",
93 target_os = "macos",
94 target_os = "ios",
95 target_os = "windows",
96)))]
97use mock as default;
98
99pub mod error;
100pub mod search;
101
102pub fn set_default_credential_search(default_search: Box<CredentialSearch>) -> Result<Search> {
103 Ok(Search {
104 inner: default_search,
105 })
106}
107
108fn default_credential_search() -> Result<Search> {
109 let credentials = default::default_credential_search();
110 Ok(Search { inner: credentials })
111}
112
113pub struct Search {
114 inner: Box<CredentialSearch>,
115}
116impl Search {
123 pub fn new() -> Result<Search> {
127 default_credential_search()
128 }
129 pub fn by_target(&self, query: &str) -> CredentialSearchResult {
140 self.inner.by("target", query)
141 }
142 pub fn by_user(&self, query: &str) -> CredentialSearchResult {
153 self.inner.by("user", query)
154 }
155 pub fn by_service(&self, query: &str) -> CredentialSearchResult {
166 self.inner.by("service", query)
167 }
168}
169
170pub struct List {}
171
172impl List {
181 pub fn list_credentials(search_result: &CredentialSearchResult, limit: Limit) -> String {
187 match limit {
188 Limit::All => Self::list_all(search_result),
189 Limit::Max(max) => Self::list_max(search_result, max),
190 }
191 }
192 fn list_all(result: &CredentialSearchResult) -> String {
197 let mut output = String::new();
198 match result {
199 Ok(search_result) => {
200 let mut entries: Vec<(String, HashMap<String, String>)> = search_result
201 .iter()
202 .map(|(k, v)| (k.clone(), v.clone()))
203 .collect();
204 entries.sort_by_key(|(k, _)| k.parse::<i32>().unwrap_or(0));
205
206 for (outer_key, inner_map) in entries {
207 output.push_str(&format!("{}\n", outer_key));
208 let mut metadata: Vec<(String, String)> = inner_map
209 .iter()
210 .map(|(k, v)| (k.clone(), v.clone()))
211 .collect();
212 metadata.sort_by(|a, b| a.0.cmp(&b.0));
213 for (key, value) in metadata {
214 output.push_str(&format!("{}: {}\n", key, value));
215 }
216 }
217 println!("Search returned {} results\n", search_result.keys().len());
218 output
219 }
220 Err(err) => err.to_string(),
221 }
222 }
223 fn list_max(result: &CredentialSearchResult, max: i64) -> String {
229 let mut output = String::new();
230 let mut count = 1;
231 match result {
232 Ok(search_result) => {
233 let mut entries: Vec<(String, HashMap<String, String>)> = search_result
234 .iter()
235 .map(|(k, v)| (k.clone(), v.clone()))
236 .collect();
237 entries.sort_by_key(|(k, _)| k.parse::<i32>().unwrap_or(0));
238
239 for (outer_key, inner_map) in entries {
240 output.push_str(&format!("{}\n", outer_key));
241 let mut metadata: Vec<(String, String)> = inner_map
242 .iter()
243 .map(|(k, v)| (k.clone(), v.clone()))
244 .collect();
245 metadata.sort_by(|a, b| a.0.cmp(&b.0));
246 for (key, value) in metadata {
247 output.push_str(&format!("{}: {}\n", key, value));
248 }
249 count += 1;
250 if count > max {
251 break;
252 }
253 }
254 println!("Search returned {} results\n", search_result.keys().len());
255 output
256 }
257 Err(err) => err.to_string(),
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 pub fn generate_random_string_of_len(len: usize) -> String {
265 use rand::{distributions::Alphanumeric, thread_rng, Rng};
268 thread_rng()
269 .sample_iter(&Alphanumeric)
270 .take(len)
271 .map(char::from)
272 .collect()
273 }
274
275 pub fn generate_random_string() -> String {
276 generate_random_string_of_len(30)
277 }
278}