This repository has no description
1use std::fmt;
2
3use axum::http::StatusCode;
4use axum::response::{IntoResponse, Response};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum PackLimit {
8 Objects,
9 ObjectBytes,
10 TotalBytes,
11 DeltaDepth,
12}
13
14impl fmt::Display for PackLimit {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 f.write_str(match self {
17 PackLimit::Objects => "object count",
18 PackLimit::ObjectBytes => "per-object size",
19 PackLimit::TotalBytes => "total decompressed size",
20 PackLimit::DeltaDepth => "delta chain depth",
21 })
22 }
23}
24
25#[derive(Debug, thiserror::Error)]
26pub enum PackError {
27 #[error("repository not found")]
28 NotFound,
29 #[error("repository index is warming, retry shortly")]
30 Unavailable,
31 #[error("invalid request path: {0}")]
32 BadPath(String),
33 #[error("unsupported service")]
34 UnsupportedService,
35 #[error("push is served over SSH, not HTTP")]
36 PushOverSsh,
37 #[error("protocol: {0}")]
38 Protocol(String),
39 #[error("unsupported request content-encoding: {0}")]
40 UnsupportedEncoding(String),
41 #[error("pkt-line: {0}")]
42 PktLine(#[from] std::io::Error),
43 #[error("pack: {0}")]
44 Pack(String),
45 #[error("pack exceeds {0} limit")]
46 LimitExceeded(PackLimit),
47 #[error("upload-pack selection exceeded its object-set limit")]
48 SelectionTooLarge,
49 #[error("upload-pack selection exceeded its time budget")]
50 SelectionTimeout,
51 #[error("insufficient memory to ingest this push, retry when the server is less busy")]
52 InsufficientMemory,
53 #[error(transparent)]
54 Git(knot_git::GitError),
55}
56
57impl From<knot_git::GitError> for PackError {
58 fn from(error: knot_git::GitError) -> Self {
59 use knot_git::SelectionLimit;
60 match error {
61 knot_git::GitError::Selection(SelectionLimit::Objects) => PackError::SelectionTooLarge,
62 knot_git::GitError::Selection(SelectionLimit::Time) => PackError::SelectionTimeout,
63 other => PackError::Git(other),
64 }
65 }
66}
67
68impl PackError {
69 pub fn http_status(&self) -> StatusCode {
70 match self {
71 PackError::NotFound => StatusCode::NOT_FOUND,
72 PackError::Unavailable => StatusCode::SERVICE_UNAVAILABLE,
73 PackError::PushOverSsh => StatusCode::FORBIDDEN,
74 PackError::BadPath(_) | PackError::UnsupportedService => StatusCode::BAD_REQUEST,
75 PackError::Protocol(_) | PackError::PktLine(_) => StatusCode::BAD_REQUEST,
76 PackError::UnsupportedEncoding(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
77 PackError::LimitExceeded(_) => StatusCode::PAYLOAD_TOO_LARGE,
78 PackError::SelectionTooLarge
79 | PackError::SelectionTimeout
80 | PackError::InsufficientMemory => StatusCode::SERVICE_UNAVAILABLE,
81 PackError::Pack(_) | PackError::Git(_) => StatusCode::INTERNAL_SERVER_ERROR,
82 }
83 }
84}
85
86impl IntoResponse for PackError {
87 fn into_response(self) -> Response {
88 (self.http_status(), self.to_string()).into_response()
89 }
90}