1use std::fmt;
2
3use thiserror::Error;
4
5#[derive(Clone, Debug, Eq, Error, PartialEq)]
6pub enum ModelError {
7 #[error("{field} must not be empty")]
8 Empty { field: &'static str },
9 #[error("{field} must not contain '/' or ASCII control characters")]
10 InvalidSegment { field: &'static str },
11 #[error("repository must have the form owner/name")]
12 InvalidRepository,
13 #[error("number must be greater than zero")]
14 InvalidNumber,
15 #[error(
16 "{field} must begin and end with an ASCII letter or digit and contain only ASCII letters, digits, '.', '_' or single '-' characters"
17 )]
18 InvalidProviderTextIdentifier { field: &'static str },
19 #[error("provider text record value must contain a positive integer 'version' field")]
20 InvalidProviderTextRecordVersion,
21 #[error("reviewer application actor must be a bot")]
22 ReviewerApplicationActorNotBot,
23 #[error("required check {name} appears more than once")]
24 DuplicateRequiredCheck { name: String },
25}
26
27pub(crate) fn segment(
28 value: impl Into<String>,
29 field: &'static str,
30) -> std::result::Result<String, ModelError> {
31 let value = value.into();
32 if value.is_empty() {
33 return Err(ModelError::Empty { field });
34 }
35 if value.contains('/') || value.chars().any(char::is_control) {
36 return Err(ModelError::InvalidSegment { field });
37 }
38 Ok(value)
39}
40
41#[derive(Clone, Debug, Eq, Error, PartialEq)]
42pub enum ProviderError {
43 #[error("{provider} does not support {operation}")]
44 Unsupported {
45 provider: &'static str,
46 operation: &'static str,
47 },
48 #[error("unrepresentable {provider} data: {fact}")]
49 Unrepresentable {
50 provider: &'static str,
51 fact: String,
52 },
53 #[error("{entity} was not found")]
54 NotFound { entity: String },
55 #[error("missing {kind} credential for identity {identity}: {entry} is absent from {origin}")]
60 MissingCredential {
61 identity: String,
62 kind: &'static str,
63 entry: String,
64 origin: ConfigurationSource,
65 },
66 #[error("provider configuration from {origin} failed: {reason}")]
67 Configuration {
68 origin: ConfigurationSource,
69 reason: String,
70 },
71 #[error("{provider} {operation} failed: {message}")]
72 External {
73 provider: &'static str,
74 operation: &'static str,
75 message: String,
76 },
77 #[error("invalid input for {provider}: {fact}")]
80 InvalidInput {
81 provider: &'static str,
82 fact: String,
83 },
84}
85
86#[derive(Clone, Debug, Eq, PartialEq)]
87pub enum ConfigurationSource {
88 Direct,
89 File(String),
90}
91
92impl fmt::Display for ConfigurationSource {
93 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 Self::Direct => formatter.write_str("direct construction"),
96 Self::File(path) => write!(formatter, "file {path}"),
97 }
98 }
99}
100
101pub type Result<T> = std::result::Result<T, ProviderError>;