aion-package 0.30.0

Archive validation, content hashing, and namespacing for Aion workflow packages.
Documentation
//! The refusals a render earns, one per honest way a parameter set and a
//! declared command can fail to meet.

use thiserror::Error;

/// Why a declared command could not be rendered into an argv.
///
/// Every variant is TERMINAL for an executing action: the same parameter set
/// against the same declaration fails identically however many times it is
/// retried. The AWL side maps each of these back onto its own cause
/// vocabulary with a source span; an executor reports the message as it
/// stands.
#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum RenderError {
    /// The parameter set names something the command does not declare.
    #[error(
        "the parameter set names `{parameter}`, which command `{command}` does not declare; a \
         value nothing consumes is a value nobody reviewed"
    )]
    ArgumentUndeclared {
        /// The command being rendered.
        command: String,
        /// The undeclared name that was supplied.
        parameter: String,
    },
    /// A declared parameter has neither a supplied value nor a default.
    #[error(
        "the parameter set omits `{parameter}`, which command `{command}` declares and gives no \
         default; there is nothing for this surface to invent"
    )]
    ArgumentMissing {
        /// The command being rendered.
        command: String,
        /// The parameter nothing filled.
        parameter: String,
    },
    /// A supplied value's shape does not match the parameter's declared type.
    #[error(
        "parameter `{parameter}` of command `{command}` is declared a {declared}, and the \
         parameter set supplies a {supplied}"
    )]
    ArgumentTypeMismatch {
        /// The command being rendered.
        command: String,
        /// The parameter whose shape did not match.
        parameter: String,
        /// The value that was supplied, as a diagnostic reads it. Carried
        /// rather than re-derived: the bytes a reader must be handed are the
        /// ones that were actually seen.
        observed: String,
        /// The declared shape, as a diagnostic reads it.
        declared: &'static str,
        /// The supplied shape, as a diagnostic reads it.
        supplied: &'static str,
    },
    /// An operand would emit leading-dash bytes into a position the program
    /// is still reading options in, with no end-of-options marker before it.
    #[error(
        "command `{command}` would emit `{element}` for argument `{argument}`, which begins with \
         `-`, and no `{marker}` argument stands before it. Nothing downstream could tell it from \
         an option"
    )]
    LeadingDashOperand {
        /// The command being rendered.
        command: String,
        /// How the declaration names the offending argument.
        argument: String,
        /// The element that would have been emitted.
        element: String,
        /// The end-of-options marker the declaration could have carried.
        marker: &'static str,
    },
    /// A command line reads a parameter the command does not declare, so the
    /// declaration and its own body disagree and no parameter set could
    /// reconcile them.
    ///
    /// Unreachable for a contract the emitter produced — the check layer
    /// refuses a hole naming an undeclared parameter before emission — so
    /// this names an archive whose stored contract was hand-edited, or one
    /// whose body and parameter list were assembled from different sources.
    /// Refused rather than rendered as empty text: an argv silently missing a
    /// value is a DIFFERENT command running under the author's name.
    #[error(
        "command `{command}` is written to put the value of `{parameter}` into one of its \
         arguments, but the command declares no parameter of that name, so there is nothing \
         anywhere that could fill it and no argument list to run; redeploying the document under \
         the current spelling re-derives a command whose body and parameters agree"
    )]
    UnboundHole {
        /// The command being rendered.
        command: String,
        /// The parameter the body reads and the declaration does not have.
        parameter: String,
    },
    /// The command was read from a prior-form archive and carries a
    /// construct the current form cannot express, so executing it could not
    /// mean what its author wrote.
    #[error(
        "command `{command}` was deployed under a prior archive form and carries {construct}, \
         which the current form does not express; the archive still reads and lists everywhere, \
         and redeploying the document under the current spelling makes the command runnable again"
    )]
    PriorFormUnrenderable {
        /// The command being rendered.
        command: String,
        /// The prior-form construct, named by the reader that found it.
        construct: String,
    },
    /// A supplied value has no unambiguous argument form.
    #[error(
        "parameter `{parameter}` is {kind}, which has no unambiguous command-argument form; \
         supply a string, number, or boolean"
    )]
    UnrepresentableValue {
        /// The parameter whose value could not be rendered.
        parameter: String,
        /// The JSON kind that was supplied.
        kind: &'static str,
    },
    /// A supplied string contains a NUL byte.
    ///
    /// Refused rather than truncated: an argument reaches the kernel as a
    /// NUL-terminated string, so a NUL inside a value would silently cut the
    /// argument short and change what the program receives.
    #[error(
        "parameter `{parameter}` contains a NUL byte, which cannot appear in a command argument"
    )]
    InteriorNul {
        /// The parameter whose value carried the NUL.
        parameter: String,
    },
}

/// How a diagnostic names a value shape.
#[must_use]
pub const fn shape_word(list: bool) -> &'static str {
    if list { "list" } else { "single value" }
}