secretfile 0.1.1

A small library for helping with loading secrets from files including systemd service credentials support
Documentation
use std::borrow::Cow;
use std::env::var;
use std::error::Error;
use std::fmt::Display;
use std::fs::read_to_string;

#[derive(Debug)]
pub enum SecretError {
    Load { path: String, error: std::io::Error },
    MissingEnvVar(String),
}

impl Display for SecretError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SecretError::Load { path, error } => {
                write!(f, "failed to load token from {path}: {error:#}")
            }
            SecretError::MissingEnvVar(var) => {
                write!(f, "environment variable {var} referenced but not set")
            }
        }
    }
}

impl Error for SecretError {}

/// Load a secret from the provided path
///
/// If the provided path includes the `$CREDENTIALS_DIRECTORY` placeholder, it will be replaced with the
/// systemd service credential directory.
///
/// any trailing whitespace will be stripped from the returned secret.
pub fn load(path: &str) -> Result<String, SecretError> {
    let file = if path.contains("$CREDENTIALS_DIRECTORY") {
        let dir = var("CREDENTIALS_DIRECTORY")
            .map_err(|_| SecretError::MissingEnvVar("$CREDENTIALS_DIRECTORY".into()))?;
        Cow::Owned(path.replace("$CREDENTIALS_DIRECTORY", &dir))
    } else {
        Cow::Borrowed(path)
    };

    let mut content = read_to_string(file.as_ref()).map_err(|error| SecretError::Load {
        path: file.into(),
        error,
    })?;

    content.truncate(content.trim_end().len()); // trim in place
    Ok(content)
}