1use std::collections::HashMap;
6use std::error::Error;
7use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
8
9use anyhow::Error as AnyError;
10
11use crate::version::Version;
12
13pub const DEFAULT_OPTION_NAME: &str = "--features";
15
16pub const DEFAULT_PREFIX: &str = "Features: ";
18
19#[derive(Debug)]
21#[non_exhaustive]
22pub enum CalcResult {
23 Null,
25 Bool(bool),
27 Version(Version),
29}
30
31pub trait Calculable: Debug {
33 fn get_value(&self, features: &HashMap<String, Version>) -> Result<CalcResult, ParseError>;
42}
43
44#[derive(Debug)]
46#[non_exhaustive]
47pub enum Mode {
48 List,
50 Single(Box<dyn Calculable + 'static>),
52 Simple(Box<dyn Calculable + 'static>),
54}
55
56#[derive(Debug)]
59#[non_exhaustive]
60pub struct Config {
61 pub option_name: String,
63 pub prefix: String,
65 pub program: String,
67 pub mode: Mode,
69}
70
71impl Default for Config {
72 #[inline]
73 fn default() -> Self {
74 Self {
75 option_name: DEFAULT_OPTION_NAME.to_owned(),
76 prefix: DEFAULT_PREFIX.to_owned(),
77 program: String::new(),
78 mode: Mode::List,
79 }
80 }
81}
82
83impl Config {
84 #[inline]
86 #[must_use]
87 pub fn with_option_name(self, option_name: String) -> Self {
88 Self {
89 option_name,
90 ..self
91 }
92 }
93 #[inline]
95 #[must_use]
96 pub fn with_prefix(self, prefix: String) -> Self {
97 Self { prefix, ..self }
98 }
99 #[inline]
101 #[must_use]
102 pub fn with_program(self, program: String) -> Self {
103 Self { program, ..self }
104 }
105 #[inline]
107 #[must_use]
108 pub fn with_mode(self, mode: Mode) -> Self {
109 Self { mode, ..self }
110 }
111}
112
113#[derive(Debug)]
115#[non_exhaustive]
116pub enum ParseError {
117 CannotCompare(String, String),
119
120 InvalidComparisonOperator(String),
122
123 Uncomparable(String, String),
125
126 ParseFailure(String, AnyError),
128
129 ParseLeftovers(String, usize),
131}
132
133impl Display for ParseError {
134 #[inline]
135 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
136 match *self {
137 Self::CannotCompare(ref left, ref right) => {
138 write!(f, "Cannot compare {left} to {right}")
139 }
140 Self::InvalidComparisonOperator(ref value) => {
141 write!(f, "Invalid comparison operator '{value}'")
142 }
143 Self::Uncomparable(ref left, ref right) => write!(
144 f,
145 "Don't know how to compare {left} to anything, including {right}"
146 ),
147 Self::ParseFailure(ref value, _) => {
148 write!(f, "Could not parse '{value}' as a valid expression")
149 }
150 Self::ParseLeftovers(ref value, ref count) => {
151 write!(
152 f,
153 "Could not parse '{value}' as a valid expression: {count} bytes left over"
154 )
155 }
156 }
157 }
158}
159
160impl Error for ParseError {
161 #[inline]
162 fn source(&self) -> Option<&(dyn Error + 'static)> {
163 match *self {
164 Self::CannotCompare(_, _)
165 | Self::InvalidComparisonOperator(_)
166 | Self::Uncomparable(_, _)
167 | Self::ParseLeftovers(_, _) => None,
168 Self::ParseFailure(_, ref err) => Some(err.as_ref()),
169 }
170 }
171}
172
173#[derive(Debug)]
175#[non_exhaustive]
176pub enum ObtainError {
177 DecodeOutput(String, AnyError),
179
180 RunProgram(String, AnyError),
182
183 Parse(ParseError),
185}
186
187impl Display for ObtainError {
188 #[inline]
189 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
190 match *self {
191 Self::DecodeOutput(ref prog, _) => write!(
192 f,
193 "Could not decode the {prog} program's output as valid UTF-8"
194 ),
195 Self::RunProgram(ref prog, _) => write!(f, "Could not execute the {prog} program"),
196 Self::Parse(_) => write!(f, "Parse error"),
197 }
198 }
199}
200
201impl Error for ObtainError {
202 #[inline]
203 fn source(&self) -> Option<&(dyn Error + 'static)> {
204 match *self {
205 Self::DecodeOutput(_, ref err) | Self::RunProgram(_, ref err) => Some(err.as_ref()),
206 Self::Parse(ref err) => Some(err),
207 }
208 }
209}
210
211#[derive(Debug)]
213#[non_exhaustive]
214pub enum Obtained {
215 Failed(ObtainError),
218 NotSupported,
220 Features(HashMap<String, Version>),
222}