iota_config/
persisted_config.rs1use std::{
8 fs,
9 io::BufWriter,
10 path::{Path, PathBuf},
11};
12
13use anyhow::{Context, Result};
14use iota_types::multiaddr::Multiaddr;
15use serde::{Serialize, de::DeserializeOwned};
16use tracing::trace;
17
18use crate::{IOTA_CONFIG_DIR, IOTA_GENESIS_FILENAME};
19
20const IOTA_DIR: &str = ".iota";
21
22pub fn iota_config_dir() -> Result<PathBuf, anyhow::Error> {
24 match std::env::var_os("IOTA_CONFIG_DIR") {
25 Some(config_env) => Ok(config_env.into()),
26 None => match dirs::home_dir() {
27 Some(v) => Ok(v.join(IOTA_DIR).join(IOTA_CONFIG_DIR)),
28 None => anyhow::bail!("cannot obtain home directory path"),
29 },
30 }
31 .and_then(|dir| {
32 if !dir.exists() {
33 fs::create_dir_all(dir.clone())?;
34 }
35 Ok(dir)
36 })
37}
38
39pub fn genesis_blob_exists(config_dir: Option<PathBuf>) -> bool {
42 if let Some(dir) = config_dir {
43 dir.join(IOTA_GENESIS_FILENAME).exists()
44 } else if let Some(config_env) = std::env::var_os("IOTA_CONFIG_DIR") {
45 Path::new(&config_env).join(IOTA_GENESIS_FILENAME).exists()
46 } else if let Some(home) = dirs::home_dir() {
47 let mut config = PathBuf::new();
48 config.push(&home);
49 config.extend([IOTA_DIR, IOTA_CONFIG_DIR, IOTA_GENESIS_FILENAME]);
50 config.exists()
51 } else {
52 false
53 }
54}
55
56pub fn validator_config_file(address: Multiaddr, i: usize) -> String {
58 multiaddr_to_filename(address).unwrap_or(format!("validator-config-{i}.yaml"))
59}
60
61pub fn ssfn_config_file(address: Multiaddr, i: usize) -> String {
64 multiaddr_to_filename(address).unwrap_or(format!("ssfn-config-{i}.yaml"))
65}
66
67fn multiaddr_to_filename(address: Multiaddr) -> Option<String> {
69 if let Some(hostname) = address.hostname() {
70 if let Some(port) = address.port() {
71 return Some(format!("{hostname}-{port}.yaml"));
72 }
73 }
74 None
75}
76
77pub trait Config
79where
80 Self: DeserializeOwned + Serialize,
81{
82 fn persisted(self, path: &Path) -> PersistedConfig<Self> {
83 PersistedConfig {
84 inner: self,
85 path: path.to_path_buf(),
86 }
87 }
88
89 fn load<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
90 let path = path.as_ref();
91 trace!("Reading config from {}", path.display());
92 let reader = fs::File::open(path)
93 .with_context(|| format!("unable to load config from {}", path.display()))?;
94 Ok(serde_yaml::from_reader(reader)?)
95 }
96
97 fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), anyhow::Error> {
98 let path = path.as_ref();
99 trace!("Writing config to {}", path.display());
100 let mut write = BufWriter::new(fs::File::create(path)?);
101 serde_yaml::to_writer(&mut write, &self)
102 .with_context(|| format!("unable to save config to {}", path.display()))?;
103 Ok(())
104 }
105}
106
107pub struct PersistedConfig<C> {
109 inner: C,
110 path: PathBuf,
111}
112
113impl<C> PersistedConfig<C>
114where
115 C: Config,
116{
117 pub fn read(path: &Path) -> Result<C, anyhow::Error> {
118 Config::load(path)
119 }
120
121 pub fn save(&self) -> Result<(), anyhow::Error> {
122 self.inner.save(&self.path)
123 }
124
125 pub fn into_inner(self) -> C {
126 self.inner
127 }
128
129 pub fn path(&self) -> &Path {
130 &self.path
131 }
132}
133
134impl<C> std::ops::Deref for PersistedConfig<C> {
135 type Target = C;
136
137 fn deref(&self) -> &Self::Target {
138 &self.inner
139 }
140}
141
142impl<C> std::ops::DerefMut for PersistedConfig<C> {
143 fn deref_mut(&mut self) -> &mut Self::Target {
144 &mut self.inner
145 }
146}