windows_native_keyring_store/
store.rs1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use keyring_core::api::{CredentialPersistence, CredentialStoreApi};
6use keyring_core::attributes::parse_attributes;
7use keyring_core::{Entry, Result};
8
9use crate::cred::Cred;
10
11#[derive(Debug, Clone)]
13pub struct Store {
14 pub id: String,
15 pub delimiters: [String; 3],
16 pub service_no_divider: bool,
17}
18
19impl Store {
20 pub fn new() -> Result<Arc<Self>> {
25 Ok(Self::new_internal(
26 ["".to_string(), ".".to_string(), "".to_string()],
27 false,
28 ))
29 }
30
31 pub fn new_with_configuration(config: &HashMap<&str, &str>) -> Result<Arc<Self>> {
39 let config = parse_attributes(
40 &["prefix", "divider", "suffix", "*service_no_divider"],
41 Some(config),
42 )?;
43 let prefix = match config.get("prefix") {
44 Some(prefix) => prefix.to_string(),
45 None => "".to_string(),
46 };
47 let divider = match config.get("divider") {
48 Some(divider) => divider.to_string(),
49 None => ".".to_string(),
50 };
51 let suffix = match config.get("suffix") {
52 Some(suffix) => suffix.to_string(),
53 None => "".to_string(),
54 };
55 let service_no_divider = config
56 .get("service_no_divider")
57 .is_some_and(|s| s.eq("true"));
58 Ok(Self::new_internal(
59 [prefix, divider, suffix],
60 service_no_divider,
61 ))
62 }
63
64 fn new_internal(delimiters: [String; 3], service_no_divider: bool) -> Arc<Self> {
65 let now = SystemTime::now();
66 let elapsed = if now.lt(&UNIX_EPOCH) {
67 UNIX_EPOCH.duration_since(now).unwrap()
68 } else {
69 now.duration_since(UNIX_EPOCH).unwrap()
70 };
71 Arc::new(Store {
72 id: format!(
73 "Crate version {}, Instantiated at {}",
74 env!("CARGO_PKG_VERSION"),
75 elapsed.as_secs_f64()
76 ),
77 delimiters,
78 service_no_divider,
79 })
80 }
81}
82
83impl CredentialStoreApi for Store {
84 fn vendor(&self) -> String {
86 "Windows Credential Manager, https://crates.io/crates/windows-native-keyring-store"
87 .to_string()
88 }
89
90 fn id(&self) -> String {
92 self.id.clone()
93 }
94
95 fn build(
100 &self,
101 service: &str,
102 user: &str,
103 modifiers: Option<&HashMap<&str, &str>>,
104 ) -> Result<Entry> {
105 let mods = parse_attributes(&["target", "persistence"], modifiers)?;
106 let target = mods.get("target").map(|s| s.as_str());
107 let persistence = mods
108 .get("persistence")
109 .map(|s| s.as_str())
110 .unwrap_or("Enterprise");
111 let cred = Cred::build_from_specifiers(
112 target,
113 &self.delimiters,
114 self.service_no_divider,
115 service,
116 user,
117 persistence.parse()?,
118 )?;
119 Ok(Entry::new_with_credential(Arc::new(cred)))
120 }
121
122 fn as_any(&self) -> &dyn std::any::Any {
124 self
125 }
126
127 fn persistence(&self) -> CredentialPersistence {
131 CredentialPersistence::UntilDelete
132 }
133
134 fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 std::fmt::Debug::fmt(self, f)
137 }
138}