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}