1use super::fix_identities;
2use super::Complete;
3use pijul_config as config;
4
5use libpijul::key::{PublicKey, SecretKey};
6
7use std::fs;
8use std::path::PathBuf;
9
10use anyhow::bail;
11use pijul_interaction::Select;
12use std::sync::OnceLock;
13
14static CHOSEN_IDENTITY: OnceLock<String> = OnceLock::new();
15
16pub fn path(name: &str, should_exist: bool) -> Result<PathBuf, anyhow::Error> {
26 let mut path = config::global_config_dir()
27 .expect("Could not find global config directory")
28 .join("identities");
29
30 if name.is_empty() {
31 bail!("Cannot get path of un-named identity");
32 }
33
34 path.push(name);
35 if !path.exists() && should_exist {
36 bail!("Cannot get identity path: name does not exist")
37 }
38
39 Ok(path)
40}
41
42pub fn public_key(name: &str) -> Result<PublicKey, anyhow::Error> {
47 let text = fs::read_to_string(path(name, true)?.join("identity.toml"))?;
48 let identity: Complete = toml::from_str(&text)?;
49
50 Ok(identity.public_key)
51}
52
53pub fn secret_key(name: &str) -> Result<SecretKey, anyhow::Error> {
58 let identity_text = fs::read_to_string(path(name, true)?.join("secret_key.json"))?;
59 let secret_key: SecretKey = serde_json::from_str(&identity_text)?;
60
61 Ok(secret_key)
62}
63
64pub async fn choose_identity_name() -> Result<String, anyhow::Error> {
69 if let Some(name) = CHOSEN_IDENTITY.get() {
70 return Ok(name.clone());
71 }
72
73 let mut possible_identities = Complete::load_all()?;
74 if possible_identities.is_empty() {
75 fix_identities().await?;
76 possible_identities = Complete::load_all()?;
77 }
78
79 let chosen_name = if possible_identities.len() == 1 {
80 possible_identities[0].clone().name
81 } else {
82 let index = Select::new()?
83 .with_prompt("Select identity")
84 .with_items(&possible_identities)
85 .with_default(0 as usize)
86 .interact()?;
87
88 possible_identities[index].clone().name
89 };
90
91 CHOSEN_IDENTITY
93 .set(chosen_name.clone())
94 .expect("Could not set chosen identity");
95
96 Ok(chosen_name)
97}
98
99impl Complete {
100 pub fn load(identity_name: &str) -> Result<Self, anyhow::Error> {
105 let identity_path = path(identity_name, true)?;
106
107 let text = fs::read_to_string(identity_path.join("identity.toml"))?;
108 let identity: Complete = toml::from_str(&text)?;
109
110 let secret_key = secret_key(identity_name)?;
111
112 Ok(Self::new(
113 identity_name.to_string(),
114 identity.config,
115 identity.public_key,
116 Some(super::Credentials::from(secret_key)),
117 ))
118 }
119
120 pub fn load_all() -> Result<Vec<Self>, anyhow::Error> {
122 let config_dir = config::global_config_dir().unwrap();
123 let identities_path = config_dir.join("identities");
124 std::fs::create_dir_all(&identities_path)?;
125
126 let identities_dir = identities_path.as_path().read_dir()?;
127 let mut identities = vec![];
128
129 for dir_entry in identities_dir {
130 let file_name = dir_entry?.file_name();
131 let identity_name = file_name.to_str().unwrap();
132
133 if let Ok(identity) = Self::load(identity_name) {
134 identities.push(identity);
135 }
136 }
137
138 Ok(identities)
139 }
140}