///|
/// A reason an update was not accepted.
///
/// Every variant is a refusal. There is no variant meaning "checked and could
/// not tell": a check that cannot reach a conclusion is a check that failed,
/// and the caller must treat it as such.
pub(all) suberror UpdateError {
  FetchFailed(url~ : String, detail~ : String)
  ManifestSignatureInvalid
  ManifestUndecodable(detail~ : String)
  ManifestStale(published_at~ : String, oldest_accepted~ : String)
  MalformedSignature(url~ : String)
  UntrustedKey(detail~ : String)
  ArtifactSizeMismatch(expected~ : Int64, actual~ : Int64)
  ArtifactDigestMismatch(expected~ : String, actual~ : String)
  ArtifactSignatureInvalid
  /// The system clock cannot produce a usable freshness bound.
  ClockUnusable(detail~ : String)
  /// Another process owns the short-lived update commit lock.
  InstallBusy
  /// A signed release is older than what is currently installed.
  RollbackRejected(target~ : UInt64, installed~ : UInt64)
  /// The app bundle does not carry the revision authenticated by the manifest.
  RevisionMismatch(detail~ : String)
  /// Waiting for the process-local install coordinator was interrupted.
  InstallInterrupted(detail~ : String)
  /// A native install step refused. `step` says how far the sequence got,
  /// which is what distinguishes "nothing was touched" from "the replacement
  /// ran and rolled back".
  InstallFailed(step~ : String, detail~ : String)
} derive(Debug, Eq)

///|
pub fn UpdateError::message(self : UpdateError) -> String {
  match self {
    FetchFailed(url~, detail~) => "cannot fetch \{url}: \{detail}"
    ManifestSignatureInvalid =>
      "the update manifest is not signed by a trusted key"
    ManifestUndecodable(detail~) =>
      "the update manifest is malformed: \{detail}"
    ManifestStale(published_at~, oldest_accepted~) =>
      "the update manifest was published at \{published_at}, older than the accepted \{oldest_accepted}"
    MalformedSignature(url~) => "the signature at \{url} is not hexadecimal"
    UntrustedKey(detail~) => "a configured trusted key is unusable: \{detail}"
    ArtifactSizeMismatch(expected~, actual~) =>
      "the artifact is \{actual} bytes, the manifest declares \{expected}"
    ArtifactDigestMismatch(expected~, actual~) =>
      "the artifact digest is \{actual}, the manifest declares \{expected}"
    ArtifactSignatureInvalid => "the artifact is not signed by a trusted key"
    ClockUnusable(detail~) =>
      "the update freshness window cannot be computed: \{detail}"
    InstallBusy => "another process is installing an update"
    RollbackRejected(target~, installed~) =>
      "update revision \{target} is older than installed revision \{installed}"
    RevisionMismatch(detail~) =>
      "the update revision could not be authenticated: \{detail}"
    InstallInterrupted(detail~) =>
      "waiting to install the update was interrupted: \{detail}"
    InstallFailed(step~, detail~) =>
      "installing the update failed while it was about to \{step}: \{detail}"
  }
}

///|
impl Show for UpdateError with fn output(self, logger) {
  logger.write_string(self.message())
}