Skip to main content

radicle_systemd/
credential.rs

1use std::env::{VarError::*, var};
2use std::ffi::OsString;
3use std::fmt;
4use std::path::{PathBuf, is_separator};
5
6const CREDENTIALS_DIRECTORY: &str = "CREDENTIALS_DIRECTORY";
7
8/// Takes a systemd credential ID. If the environment variable
9/// `CREDENTIALS_DIRECTORY` is set and valid Unicode, and the file corresponding
10/// to the credential exists, returns the path of the file corresponding to the
11/// credential.
12///
13/// Absence of the environment variable and inexistence of the file are handled
14/// gracefully returning `Ok(None)`.
15pub fn path(id: &str) -> Result<Option<PathBuf>, PathError> {
16    use PathError::*;
17
18    if id.contains(is_separator) {
19        return Err(InvalidCredentialId { id: id.to_owned() });
20    }
21
22    let credential = match var(CREDENTIALS_DIRECTORY) {
23        Err(NotUnicode(os)) => return Err(EnvVarNotUnicode { os }),
24        Err(NotPresent) => return Ok(None),
25        Ok(env) => PathBuf::from(env).join(id),
26    };
27
28    Ok(credential.exists().then_some(credential))
29}
30
31/// The error returned by [`path`].
32#[derive(Debug)]
33pub enum PathError {
34    InvalidCredentialId { id: String },
35    EnvVarNotUnicode { os: OsString },
36}
37
38impl fmt::Display for PathError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        use PathError::*;
41        match self {
42            InvalidCredentialId { id } => write!(f, "The systemd credential ID '{id}' is invalid."),
43            EnvVarNotUnicode { os } => write!(
44                f,
45                "The value of environment variable '{CREDENTIALS_DIRECTORY}' is not valid Unicode (it lossily translates to '{}').",
46                os.to_string_lossy()
47            ),
48        }
49    }
50}