feature_check/defs.rs
1// SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
2// SPDX-License-Identifier: BSD-2-Clause
3//! Common definitions for the feature-check crate's modules.
4
5use std::collections::HashMap;
6use std::fmt::Debug;
7
8use anyhow::Error as AnyError;
9use thiserror::Error;
10
11use crate::version::Version;
12
13/// The default option to pass to a program to obtain the list of features.
14pub const DEFAULT_OPTION_NAME: &str = "--features";
15
16/// The default prefix to look for in the lines output by the program.
17pub const DEFAULT_PREFIX: &str = "Features: ";
18
19/// The result of evaluating either a single term or the whole expression.
20#[derive(Debug)]
21#[non_exhaustive]
22pub enum CalcResult {
23 /// No value, e.g. the queried feature is not present.
24 Null,
25 /// A boolean value, usually for the whole expression.
26 Bool(bool),
27 /// A feature's obtained version.
28 Version(Version),
29}
30
31/// An object that may be evaluated and provide a result.
32pub trait Calculable: Debug {
33 /// Get the value of the evaluated term or expression as applied to
34 /// the list of features obtained for the program.
35 ///
36 /// # Errors
37 ///
38 /// Will propagate errors from parsing strings as [`crate::version::Version`] objects.
39 /// Will also return an error if the expression contains comparisons of
40 /// objects of incompatible types (e.g. a version string and a comparison result).
41 fn get_value(&self, features: &HashMap<String, Version>) -> Result<CalcResult, ParseError>;
42}
43
44/// The feature-check operating mode, usually "List".
45#[derive(Debug)]
46#[non_exhaustive]
47pub enum Mode {
48 /// Obtain the list of the program's features.
49 List,
50 /// Obtain the value of a single feature.
51 Single(Box<dyn Calculable + 'static>),
52 /// Evaluate a "feature op version" expression.
53 Simple(Box<dyn Calculable + 'static>),
54}
55
56/// Runtime configuration settings for
57/// the [`obtain_features`][crate::obtain::obtain_features] function.
58#[derive(Debug)]
59#[non_exhaustive]
60pub struct Config {
61 /// The option to pass to the program to query for supported features.
62 pub option_name: String,
63 /// The prefix to look for in the lines output by the program.
64 pub prefix: String,
65 /// The name or full path of the program to execute.
66 pub program: String,
67 /// The feature-check tool's operating mode.
68 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 /// Replace the option name to query.
85 #[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 /// Replace the prefix to look for in the program output.
94 #[inline]
95 #[must_use]
96 pub fn with_prefix(self, prefix: String) -> Self {
97 Self { prefix, ..self }
98 }
99 /// Replace the name of the program to execute.
100 #[inline]
101 #[must_use]
102 pub fn with_program(self, program: String) -> Self {
103 Self { program, ..self }
104 }
105 /// Replace the query mode.
106 #[inline]
107 #[must_use]
108 pub fn with_mode(self, mode: Mode) -> Self {
109 Self { mode, ..self }
110 }
111}
112
113/// Errors that can occur during parsing a test expression or the features line.
114#[derive(Debug, Error)]
115#[non_exhaustive]
116pub enum ParseError {
117 /// The arguments to a comparison operator are of incompatible types.
118 #[error("Cannot compare {0} to {1}")]
119 CannotCompare(String, String),
120
121 /// An unrecognized comparison operator was specified.
122 #[error("Invalid comparison operator '{0}'")]
123 InvalidComparisonOperator(String),
124
125 /// The arguments to a comparison operator are of unexpected types.
126 #[error("Don't know how to compare {0} to anything, including {1}")]
127 Uncomparable(String, String),
128
129 /// A parser failed.
130 #[error("Could not parse '{0}' as a valid expression")]
131 ParseFailure(String, #[source] AnyError),
132
133 /// A parser left some bytes out.
134 #[error("Could not parse '{0}' as a valid expression: {1} bytes left over")]
135 ParseLeftovers(String, usize),
136}
137
138/// Errors that can occur during querying a program's features.
139#[derive(Debug, Error)]
140#[non_exhaustive]
141pub enum ObtainError {
142 /// The program's output was not a valid UTF-8 string.
143 #[error("Could not decode the {0} program's output as valid UTF-8")]
144 DecodeOutput(String, #[source] AnyError),
145
146 /// The program could not be executed.
147 #[error("Could not execute the {0} program")]
148 RunProgram(String, #[source] AnyError),
149
150 /// A user-supplied expression could not be parsed.
151 #[error("Parse error")]
152 Parse(#[source] ParseError),
153}
154
155/// The result of querying a program for its supported features.
156#[derive(Debug)]
157#[non_exhaustive]
158pub enum Obtained {
159 /// The program could not be executed at all, or its output could
160 /// not be parsed as a reasonable string.
161 Failed(ObtainError),
162 /// The program does not support being queried for features.
163 NotSupported,
164 /// The program's supported features were successfully parsed.
165 Features(HashMap<String, Version>),
166}