aion-server 0.27.1

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
//! The identity a worker-deployment record captures for the binary it names.
//!
//! A `builtin` deployment names THIS server's executable, and the record has to
//! say which executable that was when the record was written — the content, not
//! the path, because a path is a label recording where a file used to be. The
//! supervisor captures the identity again at every spawn, and the two
//! disagreeing is how a restart across an upgrade becomes a visible fact
//! instead of a silent swap.
//!
//! This lives in the worker domain rather than on a transport because both
//! transports and auto-provision mint records, and three copies of one capture
//! is three chances for them to disagree about what a deployment's binary is.

use aion_store::DeployedBinaryIdentity;

use crate::build_identity::BuildIdentity;
use sha2::{Digest, Sha256};

/// Typed deploy-time executable identity capture failures.
#[derive(Debug, thiserror::Error)]
pub enum BinaryIdentityCaptureError {
    /// The operating system could not identify this executable.
    #[error("could not resolve the running server executable: {source}")]
    CurrentExecutable {
        /// The operating system's own diagnosis.
        #[source]
        source: std::io::Error,
    },
    /// The executable bytes could not be read.
    #[error("could not read running server executable `{path}`: {source}")]
    ReadExecutable {
        /// The executable whose bytes could not be read.
        path: std::path::PathBuf,
        /// The operating system's own diagnosis.
        #[source]
        source: std::io::Error,
    },
}

/// Capture the running server executable's identity for a deployment record.
///
/// # Errors
///
/// Returns [`BinaryIdentityCaptureError`] when the operating system cannot name
/// this executable or its bytes cannot be read. Neither is defaulted: a record
/// carrying an invented hash would report every future upgrade as a match.
pub fn capture_binary_identity() -> Result<DeployedBinaryIdentity, BinaryIdentityCaptureError> {
    let path = std::env::current_exe()
        .map_err(|source| BinaryIdentityCaptureError::CurrentExecutable { source })?;
    let bytes =
        std::fs::read(&path).map_err(|source| BinaryIdentityCaptureError::ReadExecutable {
            path: path.clone(),
            source,
        })?;
    let identity = BuildIdentity::current();
    let content_hash = lowercase_hex(&Sha256::digest(bytes));
    Ok(DeployedBinaryIdentity {
        version: identity.version.to_owned(),
        commit: identity.commit.to_owned(),
        dirty: identity.dirty.to_owned(),
        content_hash,
    })
}

/// Lowercase hexadecimal rendering of a digest.
///
/// Written out rather than pulled from a formatting helper so the wire form of
/// a content hash is decided in one place and cannot acquire a `0x`, an
/// uppercase run, or a separator from a dependency's default.
#[must_use]
pub fn lowercase_hex(bytes: &[u8]) -> String {
    const DIGITS: &[u8; 16] = b"0123456789abcdef";
    let mut encoded = String::with_capacity(bytes.len().saturating_mul(2));
    for byte in bytes {
        encoded.push(char::from(DIGITS[usize::from(byte >> 4)]));
        encoded.push(char::from(DIGITS[usize::from(byte & 0x0f)]));
    }
    encoded
}