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}