Skip to main content

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