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}