alpm_lint_config/
lint_rule_config.rs

1//! The linting configuration for all lint rules.
2use serde::{Deserialize, Serialize};
3
4/// Returns the serialized TOML representation of a default value.
5///
6/// Takes the default value for a given option and converts it into its equivalent TOML
7/// representation. This is used to expose the internal default values to users.
8///
9/// If a second argument is provided, that value will be considered as an override.
10/// This is useful for options that determine its default value based on the runtime
11/// environment.
12macro_rules! default_text {
13    ($value:expr) => {{
14        let mut text = String::new();
15        $value
16            .serialize(toml::ser::ValueSerializer::new(&mut text))
17            .expect(&format!(
18                "Failed to serialize default text: '{:?}' to toml",
19                $value
20            ));
21        text
22    }};
23    ($value:expr, $override:expr) => {
24        $override.to_string()
25    };
26}
27
28/// Creates the [`LintRuleConfiguration`] struct that defines all available lint rules.
29///
30/// Every lint rule is defined using the same structure, e.g.:
31///
32/// ```text
33/// /// This is a test option
34/// test_option: String = "This is a default",
35/// ```
36///
37/// - `/// This is a test option`: The documentation that is associated with the option.
38/// - `test_option`: The name of the option.
39/// - `: String`: The data type of the option value.
40/// - `= "This is a default"`: The default value for the option. Anything that implements [`Into`]
41///   for the target data type is accepted.
42///
43/// # Parameters
44///
45/// The macro accepts additional parameters. These have the form of `#[param_name = param_value]`
46/// and are positioned above the option declaration, for example:
47///
48/// This list of parameters is currently supported:
49///
50/// - `#[default_text = "some default"]`: Use this, if your default value depends on runtime data.
51///   This is used to set the human readable text in documentation contexts. For example set
52///   `#[default_text = "current architecture"]` for lints that default to the system's architecture
53///   that is detected during linting runtime.
54///
55///   Example:
56///   ```text
57///   /// This is a test option
58///   #[default_text = "current architecture"]
59///   architecture: Option<Architecture> = None,
60///   ```
61#[macro_export]
62macro_rules! create_lint_rule_config {
63    ($(
64        $(#[doc = $doc:literal])+
65        $(#[default_text = $default_text:expr])?
66        $name:ident: $type:ty = $default:expr,
67    )*) => {
68        use std::collections::BTreeMap;
69
70        /// Configuration struct that contains all options to adjust ALPM-related linting rules.
71        #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
72        pub struct LintRuleConfiguration {
73            $(
74                $(#[doc = $doc])+
75                pub $name: $type
76            )*
77        }
78
79        /// This module contains the default value functions for every configuration option.
80        mod defaults {
81            $(#[inline] pub fn $name() -> $type { $default.into() })*
82        }
83
84        impl Default for LintRuleConfiguration {
85            fn default() -> Self {
86                Self {
87                    $($name: defaults::$name(),)*
88                }
89            }
90        }
91
92        impl LintRuleConfiguration {
93            /// Returns the map of all configuration options with their respective name, default value
94            /// and documentation.
95            ///
96            /// This function is mainly designed to generate the public documentation of
97            /// alpm-lint and for development tooling.
98            ///
99            /// # Examples
100            ///
101            /// ```
102            /// use alpm_lint_config::LintRuleConfiguration;
103            ///
104            /// println!("{:?}", LintRuleConfiguration::configuration_options());
105            /// ```
106            pub fn configuration_options() -> BTreeMap<&'static str, LintRuleConfigurationOption> {
107                let mut map = BTreeMap::new();
108                $(
109                    map.insert(stringify!($name), LintRuleConfigurationOption {
110                        name: stringify!($name).to_string(),
111                        default: default_text!(defaults::$name() $(, $default_text)?),
112                        doc: concat!($($doc, '\n',)*),
113                    });
114                )*
115
116                map
117            }
118
119        }
120
121        /// An enum with variants representing the literal field names of [`LintRuleConfiguration`].
122        ///
123        /// The purpose of this enum is to allow lint rules to point to specific options that
124        /// they require, as we need some form of identifier for that. We cannot point to the
125        /// [`LintRuleConfiguration`] fields directly, so this is the next best thing.
126        #[derive(Debug, strum::Display)]
127        // NOTE: To convert the names to CamelCase, we would have to write a custom proc-macro, so we can
128        // run that logic during compile time. As this enum is only used for inter-linking inside our own
129        // crate, having a proc-macro would be a bit overkill.
130        #[allow(non_camel_case_types)]
131        pub enum LintRuleConfigurationOptionName {
132            $(
133                $(#[doc = $doc])+
134                $name
135            )*,
136        }
137    }
138}
139
140/// Represents a single configuration option.
141///
142/// This struct is used to do expose information about LintRule options as structure data via
143/// `LintRuleConfiguration::configuration_options`.
144#[derive(Clone, Debug, Deserialize, Serialize)]
145pub struct LintRuleConfigurationOption {
146    /// The name of the configuration option.
147    pub name: String,
148    /// The stringified `toml` value of the default value for this option.
149    pub default: String,
150    /// The documentation for this option.
151    pub doc: &'static str,
152}
153
154create_lint_rule_config! {
155    /// This is a test option
156    test_option: String = "This is an option",
157}