nu_experimental/options/
mod.rs

1#![allow(
2    private_interfaces,
3    reason = "The marker structs don't need to be exposed, only the static values."
4)]
5
6use crate::*;
7
8mod enforce_runtime_annotations;
9mod example;
10mod pipefail;
11mod reorder_cell_paths;
12
13pub(crate) type Version = (u16, u16, u16);
14
15/// Marker trait for defining experimental options.
16///
17/// Implement this trait to mark a struct as metadata for an [`ExperimentalOption`].
18/// It provides all necessary information about an experimental feature directly in code,
19/// without needing external documentation.
20///
21/// The `STATUS` field is especially important as it controls whether the feature is enabled
22/// by default and how users should interpret its reliability.
23pub(crate) trait ExperimentalOptionMarker {
24    /// Unique identifier for this experimental option.
25    ///
26    /// Must be a valid Rust identifier.
27    /// Used when parsing to toggle specific experimental options,
28    /// and may also serve as a user-facing label.
29    const IDENTIFIER: &'static str;
30
31    /// Brief description explaining what this option changes.
32    ///
33    /// Displayed to users in help messages or summaries without needing to visit external docs.
34    const DESCRIPTION: &'static str;
35
36    /// Indicates the status of an experimental status.
37    ///
38    /// Options marked [`Status::OptIn`] are disabled by default while options marked with
39    /// [`Status::OptOut`] are enabled by default.
40    /// Experimental options that stabilize should be marked as [`Status::DeprecatedDefault`] while
41    /// options that will be removed should be [`Status::DeprecatedDiscard`].
42    const STATUS: Status;
43
44    /// Nushell version since this experimental option is available.
45    ///
46    /// These three values represent major.minor.patch version.
47    /// Don't use some macro to generate this dynamically as this would defeat the purpose of having
48    /// a historic record.
49    const SINCE: Version;
50
51    /// Github issue that tracks this experimental option.
52    ///
53    /// Experimental options are expected to end their lifetime by either getting a default feature
54    /// or by getting removed.
55    /// To track this we want to have a respective issue on Github that tracks the status.
56    const ISSUE: u32;
57}
58
59// Export only the static values.
60// The marker structs are not relevant and needlessly clutter the generated docs.
61pub use enforce_runtime_annotations::ENFORCE_RUNTIME_ANNOTATIONS;
62pub use example::EXAMPLE;
63pub use pipefail::PIPE_FAIL;
64pub use reorder_cell_paths::REORDER_CELL_PATHS;
65
66// Include all experimental option statics in here.
67// This will test them and add them to the parsing list.
68
69/// A list of all available experimental options.
70///
71/// Use this to show users every experimental option, including their descriptions,
72/// identifiers, and current state.
73pub static ALL: &[&ExperimentalOption] = &[
74    &EXAMPLE,
75    &REORDER_CELL_PATHS,
76    &PIPE_FAIL,
77    &ENFORCE_RUNTIME_ANNOTATIONS,
78];
79
80#[cfg(test)]
81mod tests {
82    use std::collections::HashSet;
83
84    use super::*;
85
86    #[test]
87    fn assert_identifiers_are_unique() {
88        let list: Vec<_> = ALL.iter().map(|opt| opt.identifier()).collect();
89        let set: HashSet<_> = HashSet::from_iter(&list);
90        assert_eq!(list.len(), set.len());
91    }
92
93    #[test]
94    fn assert_identifiers_are_valid() {
95        for option in ALL {
96            let identifier = option.identifier();
97            assert!(!identifier.is_empty());
98
99            let mut chars = identifier.chars();
100            let first = chars.next().expect("not empty");
101            assert!(first.is_alphabetic());
102            assert!(first.is_lowercase());
103
104            for char in chars {
105                assert!(char.is_alphanumeric() || char == '-');
106                if char.is_alphabetic() {
107                    assert!(char.is_lowercase());
108                }
109            }
110        }
111    }
112
113    #[test]
114    fn assert_description_not_empty() {
115        for option in ALL {
116            assert!(!option.description().is_empty());
117        }
118    }
119}