Skip to main content

martin_core/tiles/passthrough/
error.rs

1//! Error types for the `passthrough` HTTP upstream source.
2
3/// Errors that can occur when proxying tiles from an upstream HTTP tile server.
4#[non_exhaustive]
5#[derive(thiserror::Error, Debug)]
6pub enum PassthroughError {
7    /// A per-tile HTTP request to the upstream failed (network, connect, or timeout).
8    #[error("Upstream request failed: {0}")]
9    Http(#[from] reqwest::Error),
10
11    /// No upstream URL was configured for the source.
12    #[error("Passthrough upstream has no URL templates configured")]
13    EmptyUrlList,
14
15    /// A configured request header name was not a valid HTTP header name.
16    #[error("Invalid header name {name:?} for passthrough upstream: {source}")]
17    InvalidHeaderName {
18        /// The offending header name as written in the config.
19        name: String,
20        /// Why the name was rejected.
21        #[source]
22        source: reqwest::header::InvalidHeaderName,
23    },
24
25    /// A configured request header value was not a valid HTTP header value.
26    #[error("Invalid value for header {name:?} for passthrough upstream: {source}")]
27    InvalidHeaderValue {
28        /// The header name whose value was rejected.
29        name: String,
30        /// Why the value was rejected.
31        #[source]
32        source: reqwest::header::InvalidHeaderValue,
33    },
34
35    /// A configured tile-URL template is missing one of the `{z}`/`{x}`/`{y}` placeholders.
36    #[error("Tile URL template {0} must contain {{z}}, {{x}} and {{y}} placeholders")]
37    InvalidUrlTemplate(String),
38
39    /// Fetching the upstream `TileJSON` document failed.
40    #[error("Failed to fetch TileJSON from {url}: {source}")]
41    TileJsonFetch {
42        /// The `TileJSON` URL that was requested.
43        url: String,
44        /// The underlying transport error.
45        #[source]
46        source: reqwest::Error,
47    },
48
49    /// The upstream returned a non-success status for the `TileJSON` document.
50    #[error("Fetching TileJSON from {url} returned HTTP status {status}")]
51    TileJsonStatus {
52        /// The `TileJSON` URL that was requested.
53        url: String,
54        /// The HTTP status code returned by the upstream.
55        status: u16,
56    },
57
58    /// The upstream `TileJSON` document could not be parsed.
59    #[error("Failed to parse TileJSON from {url}: {source}")]
60    TileJsonParse {
61        /// The `TileJSON` URL that was requested.
62        url: String,
63        /// The underlying deserialization error.
64        #[source]
65        source: serde_json::Error,
66    },
67
68    /// The upstream `TileJSON` document contained no entries in its `tiles` array.
69    #[error("TileJSON from {0} contained no tile URL templates")]
70    NoTilesInTileJson(String),
71
72    /// The tile format could not be derived from config, URL extension, or `TileJSON`.
73    #[error(
74        "Cannot determine tile format for passthrough source {0}; set `format` explicitly in the config"
75    )]
76    FormatUndeterminable(String),
77
78    /// A per-tile request returned an unexpected (non-success, non-404/204) HTTP status.
79    #[error("Upstream {url} returned unexpected HTTP status {status}")]
80    UnexpectedStatus {
81        /// The tile URL that was requested.
82        url: String,
83        /// The HTTP status code returned by the upstream.
84        status: u16,
85    },
86}
87
88impl crate::Classify for PassthroughError {
89    fn kind(&self) -> crate::ErrorKind {
90        use crate::ErrorKind::{Internal, Unavailable};
91        match self {
92            Self::Http(_)
93            | Self::TileJsonFetch { .. }
94            | Self::TileJsonStatus { .. }
95            | Self::TileJsonParse { .. }
96            | Self::UnexpectedStatus { .. } => Unavailable,
97            Self::EmptyUrlList
98            | Self::InvalidHeaderName { .. }
99            | Self::InvalidHeaderValue { .. }
100            | Self::InvalidUrlTemplate(_)
101            | Self::NoTilesInTileJson(_)
102            | Self::FormatUndeterminable(_) => Internal,
103        }
104    }
105}