Skip to main content

nominal/
error.rs

1use crate::core::datetime::NominalDateTimeError;
2use crate::core::rid::RidConversionError;
3use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
6
7#[non_exhaustive]
8#[derive(Debug, Error)]
9pub enum Error {
10    #[error("I/O error: {0}")]
11    Io(#[from] std::io::Error),
12
13    #[error("could not determine home directory")]
14    HomeDirNotFound,
15
16    #[error("YAML parse error: {0}")]
17    Yaml(#[from] serde_yaml::Error),
18
19    #[error("Conjure error: {details}")]
20    Conjure {
21        details: String,
22        status: Option<u16>,
23    },
24
25    #[error("Workspace not provided, but there is no default workspace for the user.")]
26    NoDefaultWorkspace,
27
28    #[error("RID conversion error: invalid RID '{rid}': {reason}")]
29    Rid { rid: String, reason: String },
30
31    #[error("seconds_since_epoch out of range: {0}")]
32    TimestampSecondsOutOfRange(i64),
33
34    #[error("offset_nanoseconds out of range: {0}")]
35    TimestampNanosOutOfRange(i64),
36
37    #[error("invalid timestamp: seconds={seconds}, nanos={nanos}")]
38    InvalidTimestamp { seconds: i64, nanos: i64 },
39
40    #[error("invalid bearer token: {reason}")]
41    InvalidBearerToken { reason: String },
42
43    #[error("invalid service URL '{url}': {reason}")]
44    InvalidServiceUrl { url: String, reason: String },
45
46    #[error("profile '{name}' not found in config")]
47    ProfileNotFound { name: String },
48
49    #[error(
50        "no config file found at {path}: create with `nomctl config profile add` or `nomctl config init`"
51    )]
52    ConfigNotFound { path: String },
53
54    #[error("missing 'version' key in config file: {path}")]
55    ConfigMissingVersion { path: String },
56
57    #[error("unsupported config version: {version} (expected 2)")]
58    ConfigUnsupportedVersion { version: u32, path: String },
59
60    #[error("environment variable '{name}' is not set")]
61    EnvVarNotSet { name: &'static str },
62
63    #[error("resource not found: {resource}")]
64    NotFound { resource: &'static str },
65
66    #[error("channel data type missing from server response for channel '{channel}'")]
67    MissingChannelDataType { channel: String },
68
69    #[error("unsupported channel data type for metadata upsert: {data_type}")]
70    UnsupportedChannelDataType { data_type: String },
71
72    #[error("multipart upload failed: {details}")]
73    Upload { details: String },
74
75    #[error("ingest error: {details}")]
76    Ingest { details: String },
77}
78
79impl From<RidConversionError> for Error {
80    fn from(value: RidConversionError) -> Self {
81        Self::Rid {
82            rid: value.rid().to_string(),
83            reason: value.reason().to_string(),
84        }
85    }
86}
87
88impl From<NominalDateTimeError> for Error {
89    fn from(value: NominalDateTimeError) -> Self {
90        match value {
91            NominalDateTimeError::SecondsOutOfRange(v) => Self::TimestampSecondsOutOfRange(v),
92            NominalDateTimeError::NanosOutOfRange(v) => Self::TimestampNanosOutOfRange(v),
93            NominalDateTimeError::InvalidTimestamp { seconds, nanos } => {
94                Self::InvalidTimestamp { seconds, nanos }
95            }
96        }
97    }
98}
99
100impl From<conjure_error::Error> for Error {
101    fn from(value: conjure_error::Error) -> Self {
102        let status = value
103            .cause()
104            .downcast_ref::<conjure_runtime::errors::RemoteError>()
105            .map(|remote| remote.status().as_u16());
106        Self::Conjure {
107            details: format!("{value:?}"),
108            status,
109        }
110    }
111}
112
113impl Error {
114    pub fn http_status(&self) -> Option<u16> {
115        match self {
116            Self::Conjure { status, .. } => *status,
117            _ => None,
118        }
119    }
120}