atproto-record 0.9.0

AT Protocol record signature operations - cryptographic signing and verification for AT Protocol records
Documentation
//! Structured error types for AT Protocol record operations.
//!
//! Comprehensive error handling for AT Protocol record signature verification and AT-URI parsing
//! operations using structured error types with the `thiserror` library. All errors follow the
//! project convention of prefixed error codes with descriptive messages.
//!
//! ## Error Categories
//!
//! - **`VerificationError`** (verification-1 to verification-11): Record signature verification and creation errors
//! - **`AturiError`** (aturi-1 to aturi-9): AT-URI parsing and validation errors
//! - **`CliError`** (cli-1 to cli-8): Command-line interface specific errors including file I/O, DID validation, and argument parsing
//!
//! ## Error Format
//!
//! All errors use the standardized format: `error-atproto-record-{domain}-{number} {message}: {details}`

use thiserror::Error;

/// Represents errors that can occur during record signature verification operations.
///
/// These errors relate to various stages of the signature verification process
/// for AT Protocol records, from parsing to cryptographic validation.
#[derive(Debug, Error)]
pub enum VerificationError {
    /// Error when no signatures field is found in the record.
    ///
    /// This error occurs when a record does not contain either a "signatures"
    /// or "sigs" field, which is required for signature verification.
    #[error("error-atproto-record-verification-1 No signatures field found in record")]
    NoSignaturesField,

    /// Error when issuer field is missing from a signature object.
    ///
    /// This error occurs when a signature object in the signatures array
    /// does not contain the required "issuer" field.
    #[error("error-atproto-record-verification-2 Missing issuer field in signature object")]
    MissingIssuerField,

    /// Error when signature field is missing from a signature object.
    ///
    /// This error occurs when a signature object in the signatures array
    /// does not contain the required "signature" field.
    #[error("error-atproto-record-verification-3 Missing signature field in signature object")]
    MissingSignatureField,

    /// Error when record serialization fails.
    ///
    /// This error occurs when the signed record cannot be serialized
    /// to IPLD CBOR format for signature verification.
    #[error("error-atproto-record-verification-4 Record serialization failed: {error}")]
    RecordSerializationFailed {
        /// The underlying serialization error
        #[from]
        error: serde_ipld_dagcbor::EncodeError<std::collections::TryReserveError>,
    },

    /// Error when signature verification fails.
    ///
    /// This error occurs when the cryptographic verification of the
    /// signature against the signed record fails, indicating the
    /// signature is invalid for the given data and public key.
    #[error("error-atproto-record-verification-5 Signature verification failed: {error}")]
    SignatureVerificationFailed {
        /// The underlying verification error
        error: anyhow::Error,
    },

    /// Error when no valid signature is found for the specified issuer.
    ///
    /// This error occurs when none of the signatures in the record
    /// belong to the specified issuer, or when all signatures from
    /// the issuer fail verification.
    #[error("error-atproto-record-verification-6 No valid signature found for issuer: {issuer}")]
    NoValidSignatureForIssuer {
        /// The issuer DID for which no valid signature was found
        issuer: String,
    },

    /// Error when signature object is not a valid JSON object.
    ///
    /// This error occurs when the provided signature object parameter
    /// is not a JSON object type, which is required for signature creation.
    #[error("error-atproto-record-verification-7 Signature object must be a JSON object")]
    InvalidSignatureObjectType,

    /// Error when signature object is missing required fields.
    ///
    /// This error occurs when the signature object is missing fields that are
    /// necessary during signature creation, such as 'issuer' or
    /// 'issuedAt'.
    #[error("error-atproto-record-verification-8 Signature object missing field: {field}")]
    SignatureObjectMissingField {
        /// The name of the missing field
        field: String,
    },

    /// Error when cryptographic key operations fail.
    ///
    /// This error occurs when key-related operations such as signing
    /// or key parsing fail during signature creation or verification.
    #[error("error-atproto-record-verification-9 Key operation failed: {error}")]
    KeyOperationFailed {
        /// The underlying key operation error
        #[from]
        error: atproto_identity::errors::KeyError,
    },

    /// Error when signature decoding fails.
    ///
    /// This error occurs when the multibase-encoded signature cannot
    /// be decoded, typically due to invalid encoding format.
    #[error("error-atproto-record-verification-10 Signature decoding failed: {error}")]
    SignatureDecodingFailed {
        /// The underlying multibase decoding error
        error: multibase::Error,
    },

    /// Error when cryptographic signature validation fails.
    ///
    /// This error occurs when the cryptographic validation of the signature
    /// fails, indicating either an invalid signature or mismatched key/data.
    #[error("error-atproto-record-verification-11 Cryptographic validation failed: {error}")]
    CryptographicValidationFailed {
        /// The underlying validation error
        error: atproto_identity::errors::KeyError,
    },
}

/// Represents errors that can occur during AT-URI parsing operations.
///
/// These errors relate to various stages of parsing AT-URIs into their
/// component parts (authority, collection, record key).
#[derive(Debug, Error)]
pub enum AturiError {
    /// Error when AT-URI does not start with the required "at://" prefix.
    ///
    /// This error occurs when the input string does not begin with the
    /// required AT-URI scheme identifier.
    #[error("error-atproto-record-aturi-1 Invalid AT-URI format: missing 'at://' prefix")]
    MissingPrefix,

    /// Error when AT-URI ends with a trailing slash.
    ///
    /// This error occurs when the AT-URI string ends with a forward slash,
    /// which is not permitted in valid AT-URI format.
    #[error("error-atproto-record-aturi-2 Invalid AT-URI format: trailing slash not permitted")]
    TrailingSlash,

    /// Error when authority component is missing from AT-URI.
    ///
    /// This error occurs when the AT-URI does not contain a valid authority
    /// component after the "at://" prefix.
    #[error("error-atproto-record-aturi-3 Authority component missing from AT-URI")]
    AuthorityMissing,

    /// Error when authority is a handle instead of a DID.
    ///
    /// This error occurs when the authority component is a handle rather
    /// than a DID, which is not supported for AT-URI operations.
    #[error("error-atproto-record-aturi-4 Unsupported authority type: handle not permitted")]
    HandleNotSupported,

    /// Error when authority component cannot be parsed as a valid DID.
    ///
    /// This error occurs when the authority component is not a valid
    /// handle or DID, or when DID parsing fails.
    #[error("error-atproto-record-aturi-5 Authority parsing failed: {error}")]
    AuthorityParsingFailed {
        /// The underlying parsing error
        #[from]
        error: atproto_identity::errors::ResolveError,
    },

    /// Error when collection (NSID) component is missing from AT-URI.
    ///
    /// This error occurs when the AT-URI does not contain a collection
    /// component after the authority.
    #[error("error-atproto-record-aturi-6 Collection component missing from AT-URI")]
    CollectionMissing,

    /// Error when record key component is missing from AT-URI.
    ///
    /// This error occurs when the AT-URI does not contain a record key
    /// component after the collection.
    #[error("error-atproto-record-aturi-7 Record key component missing from AT-URI")]
    RecordKeyMissing,

    /// Error when collection (NSID) component is empty in AT-URI.
    ///
    /// This error occurs when the AT-URI contains an empty or whitespace-only
    /// collection component, which is not valid.
    #[error("error-atproto-record-aturi-8 Collection component cannot be empty")]
    EmptyCollection,

    /// Error when record key component is empty in AT-URI.
    ///
    /// This error occurs when the AT-URI contains an empty or whitespace-only
    /// record key component, which is not valid.
    #[error("error-atproto-record-aturi-9 Record key component cannot be empty")]
    EmptyRecordKey,
}

/// Error types that can occur in CLI tools.
///
/// These errors represent failures specific to command-line interface operations
/// such as file I/O, JSON parsing, DID validation, and missing required arguments.
#[derive(Debug, Error)]
pub enum CliError {
    /// Occurs when DID method is not supported
    #[error("error-atproto-record-cli-1 Unsupported DID method: {method}")]
    UnsupportedDidMethod {
        /// The unsupported DID method
        method: String,
    },

    /// Occurs when DID parsing fails
    #[error("error-atproto-record-cli-2 Failed to parse DID: {did}")]
    DidParseFailed {
        /// The DID that failed to parse
        did: String,
    },

    /// Occurs when reading from stdin fails
    #[error("error-atproto-record-cli-3 Failed to read from stdin")]
    StdinReadFailed,

    /// Occurs when parsing JSON from stdin fails
    #[error("error-atproto-record-cli-4 Failed to parse JSON from stdin")]
    StdinJsonParseFailed,

    /// Occurs when reading a file fails
    #[error("error-atproto-record-cli-5 Failed to read file: {path}")]
    FileReadFailed {
        /// The file path that failed to read
        path: String,
    },

    /// Occurs when parsing JSON from a file fails
    #[error("error-atproto-record-cli-6 Failed to parse JSON from file: {path}")]
    FileJsonParseFailed {
        /// The file path containing invalid JSON
        path: String,
    },

    /// Occurs when an unexpected argument is provided
    #[error("error-atproto-record-cli-7 Unexpected argument: {argument}")]
    UnexpectedArgument {
        /// The unexpected argument
        argument: String,
    },

    /// Occurs when a required value is missing
    #[error("error-atproto-record-cli-8 Missing required value: {name}")]
    MissingRequiredValue {
        /// The name of the missing value
        name: String,
    },
}