sequoia_ipc/core.rs
1//! Contexts and errors.
2//!
3//! Sequoia tries to be useful for a wide variety of applications.
4//! Therefore, we need you to provide a little information about the
5//! context you are using Sequoia in.
6//!
7//! # Examples
8//!
9//! A context with reasonable defaults can be created using
10//! `Context::new`:
11//!
12//! ```no_run
13//! # use sequoia_ipc::{Context, Result};
14//! # fn main() -> Result<()> {
15//! let c = Context::new();
16//! # Ok(())
17//! # }
18//! ```
19
20#![warn(missing_docs)]
21
22use std::path::{Path, PathBuf};
23
24use crate::Result;
25
26/// A `Context` for Sequoia.
27///
28/// # Examples
29///
30/// A context with reasonable defaults can be created using
31/// `Context::new`:
32///
33/// ```no_run
34/// # use sequoia_ipc::{Context, Result};
35/// # fn main() -> Result<()> {
36/// let c = Context::new()?;
37/// # Ok(())
38/// # }
39/// ```
40///
41/// A context can be configured using the builder pattern with
42/// `Context::configure`:
43///
44/// ```
45/// # use sequoia_ipc::{Context, IPCPolicy, Result};
46/// # fn main() -> Result<()> {
47/// let c = Context::configure()
48/// # .ephemeral()
49/// .ipc_policy(IPCPolicy::Robust)
50/// .build()?;
51/// # Ok(())
52/// # }
53/// ```
54pub struct Context {
55 home: PathBuf,
56 lib: PathBuf,
57 ipc_policy: IPCPolicy,
58 ephemeral: bool,
59 cleanup: bool,
60}
61
62impl Clone for Context {
63 fn clone(&self) -> Self {
64 Context {
65 home: self.home.clone(),
66 lib: self.lib.clone(),
67 ipc_policy: self.ipc_policy,
68 ephemeral: self.ephemeral,
69 cleanup: false, // Prevent cleanup.
70 }
71 }
72}
73
74impl Drop for Context {
75 fn drop(&mut self) {
76 use std::fs::remove_dir_all;
77
78 if self.ephemeral && self.cleanup {
79 let _ = remove_dir_all(&self.home);
80 }
81 }
82}
83
84/// Returns $PREXIX at compile-time, or a reasonable default prefix.
85fn prefix() -> PathBuf {
86 /* XXX: Windows support. */
87 PathBuf::from(option_env!("PREFIX").unwrap_or("/usr/local"))
88}
89
90impl Context {
91 /// Creates a Context with reasonable defaults.
92 pub fn new() -> Result<Self> {
93 Self::configure().build()
94 }
95
96 /// Creates a Context that can be configured.
97 ///
98 /// The configuration is seeded like in `Context::new`, but can be
99 /// modified. A configuration has to be finalized using
100 /// `.build()` in order to turn it into a Context.
101 pub fn configure() -> Config {
102 Config(Context {
103 home: PathBuf::from(""), // Defer computation of default.
104 lib: prefix().join("lib").join("sequoia"),
105 ipc_policy: IPCPolicy::Robust,
106 ephemeral: false,
107 cleanup: false,
108 })
109 }
110
111 /// Returns the directory containing shared state.
112 pub fn home(&self) -> &Path {
113 &self.home
114 }
115
116 /// Returns the directory containing backend servers.
117 pub fn lib(&self) -> &Path {
118 &self.lib
119 }
120
121 /// Returns the IPC policy.
122 pub fn ipc_policy(&self) -> &IPCPolicy {
123 &self.ipc_policy
124 }
125
126 /// Returns whether or not this is an ephemeral context.
127 pub fn ephemeral(&self) -> bool {
128 self.ephemeral
129 }
130}
131
132/// Represents a `Context` configuration.
133///
134/// A context can be configured using the builder pattern with
135/// `Context::configure`:
136///
137/// ```
138/// # use sequoia_ipc::{Context, IPCPolicy, Result};
139/// # fn main() -> Result<()> {
140/// let c = Context::configure()
141/// # .ephemeral()
142/// .ipc_policy(IPCPolicy::Robust)
143/// .build()?;
144/// # Ok(())
145/// # }
146/// ```
147///
148/// You can create ephemeral context that are useful for tests and
149/// one-shot programs:
150///
151/// ```
152/// # use sequoia_ipc::{Context, Result};
153/// # use std::path::Path;
154/// # fn main() -> Result<()> {
155/// let c = Context::configure().ephemeral().build()?;
156/// let ephemeral_home = c.home().to_path_buf();
157/// // Do some tests.
158/// drop(c);
159/// assert!(! ephemeral_home.exists());
160/// # Ok(())
161/// # }
162/// ```
163pub struct Config(Context);
164
165impl Config {
166 /// Finalizes the configuration and returns a `Context`.
167 pub fn build(self) -> Result<Context> {
168 let mut c = self.0;
169
170 // As a special case, we defer the computation of the default
171 // home, because env::home_dir() may fail.
172 let home_not_set = c.home == PathBuf::from("");
173
174 // If we have an ephemeral home, and home is not explicitly
175 // set, create a temporary directory. Ephemeral contexts can
176 // share home directories, e.g. client and server processes
177 // share one home.
178 if c.ephemeral && home_not_set {
179 let tmp = tempfile::Builder::new().prefix("sequoia").tempdir()?;
180 c.home = tmp.into_path();
181 c.cleanup = true;
182 } else if home_not_set {
183 c.home = dirs::home_dir()
184 .ok_or_else(|| anyhow::anyhow!("Failed to get users home directory"))?
185 .join(".sequoia");
186 }
187 Ok(c)
188 }
189
190 /// Sets the directory containing shared state.
191 pub fn home<P: AsRef<Path>>(mut self, home: P) -> Self {
192 self.set_home(home);
193 self
194 }
195
196 /// Sets the directory containing shared state and returns the old
197 /// home directory.
198 pub fn set_home<P: AsRef<Path>>(&mut self, home: P) -> PathBuf {
199 ::std::mem::replace(&mut self.0.home, PathBuf::new().join(home))
200 }
201
202 /// Sets the directory containing backend servers.
203 pub fn lib<P: AsRef<Path>>(mut self, lib: P) -> Self {
204 self.set_lib(lib);
205 self
206 }
207
208 /// Sets the directory containing backend servers and returns the
209 /// old library directory.
210 pub fn set_lib<P: AsRef<Path>>(&mut self, lib: P) -> PathBuf {
211 ::std::mem::replace(&mut self.0.lib, PathBuf::new().join(lib))
212 }
213
214 /// Sets the IPC policy.
215 pub fn ipc_policy(mut self, policy: IPCPolicy) -> Self {
216 self.set_ipc_policy(policy);
217 self
218 }
219
220 /// Sets the IPC policy and returns the old IPC policy.
221 pub fn set_ipc_policy(&mut self, policy: IPCPolicy) -> IPCPolicy {
222 ::std::mem::replace(&mut self.0.ipc_policy, policy)
223 }
224
225 /// Makes this context ephemeral.
226 pub fn ephemeral(mut self) -> Self {
227 self.set_ephemeral();
228 self
229 }
230
231 /// Makes this context ephemeral and returns the old ephemeral
232 /// state.
233 pub fn set_ephemeral(&mut self) -> bool {
234 ::std::mem::replace(&mut self.0.ephemeral, true)
235 }
236}
237
238/* IPC policy. */
239
240/// IPC policy for Sequoia.
241///
242/// With this policy you can control how Sequoia starts background
243/// servers.
244#[derive(PartialEq, Debug, Copy, Clone)]
245pub enum IPCPolicy {
246 /// External background servers only.
247 ///
248 /// We will always use external background servers. If starting
249 /// one fails, the operation will fail.
250 ///
251 /// The advantage is that we never spawn a thread.
252 ///
253 /// The disadvantage is that we need to locate the background
254 /// server to start. If you are distribute Sequoia with your
255 /// application, make sure to include the binaries, and to
256 /// configure the Context so that `context.lib()` points to the
257 /// directory containing the binaries.
258 External,
259
260 /// Internal background servers only.
261 ///
262 /// We will always use internal background servers. It is very
263 /// unlikely that this fails.
264 ///
265 /// The advantage is that this method is very robust. If you
266 /// distribute Sequoia with your application, you do not need to
267 /// ship the binary, and it does not matter what `context.lib()`
268 /// points to. This is very robust and convenient.
269 ///
270 /// The disadvantage is that we spawn a thread in your
271 /// application. Threads may play badly with `fork(2)`, file
272 /// handles, and locks. If you are not doing anything fancy,
273 /// however, and only use fork-then-exec, you should be okay.
274 Internal,
275
276 /// Prefer external, fall back to internal.
277 ///
278 /// We will first try to use an external background server, but
279 /// fall back on an internal one should that fail.
280 ///
281 /// The advantage is that if Sequoia is properly set up to find
282 /// the background servers, we will use these and get the
283 /// advantages of that approach. Because we fail back on using an
284 /// internal server, we gain the robustness of that approach.
285 ///
286 /// The disadvantage is that we may or may not spawn a thread in
287 /// your application. If this is unacceptable in your
288 /// environment, use the `External` policy.
289 Robust,
290}
291
292impl<'a> From<&'a IPCPolicy> for u8 {
293 fn from(policy: &IPCPolicy) -> Self {
294 match policy {
295 IPCPolicy::External => 0,
296 IPCPolicy::Internal => 1,
297 IPCPolicy::Robust => 2,
298 }
299 }
300}
301
302// XXX: TryFrom would be nice.
303impl From<u8> for IPCPolicy {
304 fn from(policy: u8) -> Self {
305 match policy {
306 0 => IPCPolicy::External,
307 1 => IPCPolicy::Internal,
308 2 => IPCPolicy::Robust,
309 n => panic!("Bad IPC policy: {}", n),
310 }
311 }
312}