1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#![allow(missing_docs)]

use axum::{http::header, http::StatusCode, response::IntoResponse, Json};
use serde::Serialize;

use crate::{manager::ShareIoError, reader::TableReaderError};

#[derive(Debug, Clone, PartialEq)]
pub enum ServerError {
    // input validation errors
    InvalidPaginationParameters { reason: String },
    InvalidTableVersion,
    InvalidTableDataPredicates,
    InvalidTableChangePredicates,
    InvalidTableStartingTimestamp,
    InvalidTableVersionRange { reason: String },
    // share IO errors
    InvalidPaginationToken { reason: String },
    ShareNotFound { name: String },
    SchemaNotFound { name: String },
    TableNotFound { name: String },
    ShareManagerError { reason: String },
    // table IO errors
    TableReaderError { reason: String },
    // sharing configuration errors
    UnsupportedTableFormat { format: String },
    UnsupportedTableStorage { storage: String },
    UnsupportedOperation { reason: String },
}

impl ServerError {
    pub fn into_error_response(self) -> ErrorResponse {
        match self {
            ServerError::InvalidPaginationParameters { .. } => ErrorResponse {
                error_code: String::from("INVALID_PARAMETER_VALUE"),
                message: String::from("the `pageToken` or `maxResults` parameter is invalid"),
            },
            ServerError::InvalidPaginationToken { .. } => ErrorResponse {
                error_code: String::from("INVALID_PARAMETER_VALUE"),
                message: String::from("the `pageToken` query parameter is invalid"),
            },
            ServerError::ShareNotFound { name } => ErrorResponse {
                error_code: String::from("RESOURCE_DOES_NOT_EXIST"),
                message: format!("share `{}` not found", name),
            },
            ServerError::SchemaNotFound { name } => ErrorResponse {
                error_code: String::from("RESOURCE_DOES_NOT_EXIST"),
                message: format!("schema `{}` not found", name),
            },
            ServerError::TableNotFound { name } => ErrorResponse {
                error_code: String::from("RESOURCE_DOES_NOT_EXIST"),
                message: format!("table `{}` not found", name),
            },
            ServerError::ShareManagerError { .. } => ErrorResponse {
                error_code: String::from("INTERNAL_ERROR"),
                message: String::new(),
            },

            _ => ErrorResponse {
                error_code: String::from("Something went wrong"),
                message: String::from("check your code"),
            },
        }
    }
}

pub type Result<T> = core::result::Result<T, ServerError>;

impl From<ShareIoError> for ServerError {
    fn from(value: ShareIoError) -> Self {
        match value {
            ShareIoError::MalformedContinuationToken => ServerError::InvalidPaginationToken {
                reason: value.to_string(),
            },
            ShareIoError::ShareNotFound { share_name } => {
                ServerError::ShareNotFound { name: share_name }
            }
            ShareIoError::SchemaNotFound {
                share_name,
                schema_name,
            } => ServerError::TableNotFound {
                name: format!("{}.{}", share_name, schema_name),
            },
            ShareIoError::TableNotFound {
                share_name,
                schema_name,
                table_name,
            } => Self::TableNotFound {
                name: format!("{}.{}.{}", share_name, schema_name, table_name),
            },
            ShareIoError::ConnectionError => ServerError::ShareManagerError {
                reason: String::new(),
            },
            ShareIoError::Other { reason } => ServerError::ShareManagerError { reason },
        }
    }
}

impl From<TableReaderError> for ServerError {
    fn from(value: TableReaderError) -> Self {
        ServerError::TableReaderError {
            reason: value.to_string(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
    error_code: String,
    message: String,
}

impl IntoResponse for ServerError {
    fn into_response(self) -> axum::response::Response {
        match self {
            Self::ShareNotFound { .. } => (
                StatusCode::NOT_FOUND,
                [(
                    header::CONTENT_TYPE.as_str(),
                    "application/json; charset=utf-8",
                )],
                Json(self.into_error_response()),
            )
                .into_response(),
            Self::TableNotFound { .. } => (
                StatusCode::NOT_FOUND,
                [(
                    header::CONTENT_TYPE.as_str(),
                    "application/json; charset=utf-8",
                )],
                Json(self.into_error_response()),
            )
                .into_response(),
            _ => (
                StatusCode::BAD_REQUEST,
                [(
                    header::CONTENT_TYPE.as_str(),
                    "application/json; charset=utf-8",
                )],
                Json(self.into_error_response()),
            )
                .into_response(),
        }
    }
}