nu_cmd_lang/core_commands/
version.rs1use std::{borrow::Cow, sync::OnceLock};
2
3use itertools::Itertools;
4use nu_engine::command_prelude::*;
5use nu_protocol::engine::StateWorkingSet;
6use shadow_rs::shadow;
7
8shadow!(build);
9
10pub static VERSION_NU_FEATURES: OnceLock<Vec<Cow<'static, str>>> = OnceLock::new();
45
46#[derive(Clone)]
47pub struct Version;
48
49impl Command for Version {
50 fn name(&self) -> &str {
51 "version"
52 }
53
54 fn signature(&self) -> Signature {
55 Signature::build("version")
56 .input_output_types(vec![(Type::Nothing, Type::record())])
57 .allow_variants_without_examples(true)
58 .category(Category::Core)
59 }
60
61 fn description(&self) -> &str {
62 "Display Nu version, and its build configuration."
63 }
64
65 fn is_const(&self) -> bool {
66 true
67 }
68
69 fn run(
70 &self,
71 engine_state: &EngineState,
72 _stack: &mut Stack,
73 call: &Call,
74 _input: PipelineData,
75 ) -> Result<PipelineData, ShellError> {
76 version(engine_state, call.head)
77 }
78
79 fn run_const(
80 &self,
81 working_set: &StateWorkingSet,
82 call: &Call,
83 _input: PipelineData,
84 ) -> Result<PipelineData, ShellError> {
85 version(working_set.permanent(), call.head)
86 }
87
88 fn examples(&self) -> Vec<Example> {
89 vec![Example {
90 description: "Display Nu version",
91 example: "version",
92 result: None,
93 }]
94 }
95}
96
97fn push_non_empty(record: &mut Record, name: &str, value: &str, span: Span) {
98 if !value.is_empty() {
99 record.push(name, Value::string(value, span))
100 }
101}
102
103pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, ShellError> {
104 let mut record = Record::with_capacity(17);
123
124 record.push("version", Value::string(env!("CARGO_PKG_VERSION"), span));
125
126 push_version_numbers(&mut record, span);
127
128 push_non_empty(&mut record, "pre", build::PKG_VERSION_PRE, span);
129
130 record.push("branch", Value::string(build::BRANCH, span));
131
132 if let Some(commit_hash) = option_env!("NU_COMMIT_HASH") {
133 record.push("commit_hash", Value::string(commit_hash, span));
134 }
135
136 push_non_empty(&mut record, "build_os", build::BUILD_OS, span);
137 push_non_empty(&mut record, "build_target", build::BUILD_TARGET, span);
138 push_non_empty(&mut record, "rust_version", build::RUST_VERSION, span);
139 push_non_empty(&mut record, "rust_channel", build::RUST_CHANNEL, span);
140 push_non_empty(&mut record, "cargo_version", build::CARGO_VERSION, span);
141 push_non_empty(&mut record, "build_time", build::BUILD_TIME, span);
142 push_non_empty(
143 &mut record,
144 "build_rust_channel",
145 build::BUILD_RUST_CHANNEL,
146 span,
147 );
148
149 record.push("allocator", Value::string(global_allocator(), span));
150
151 record.push(
152 "features",
153 Value::string(
154 VERSION_NU_FEATURES
155 .get()
156 .as_ref()
157 .map(|v| v.as_slice())
158 .unwrap_or_default()
159 .iter()
160 .filter(|f| !f.starts_with("dep:"))
161 .join(", "),
162 span,
163 ),
164 );
165
166 #[cfg(not(feature = "plugin"))]
167 let _ = engine_state;
168
169 #[cfg(feature = "plugin")]
170 {
171 let installed_plugins = engine_state
173 .plugins()
174 .iter()
175 .map(|x| {
176 let name = x.identity().name();
177 if let Some(version) = x.metadata().and_then(|m| m.version) {
178 format!("{name} {version}")
179 } else {
180 name.into()
181 }
182 })
183 .collect::<Vec<_>>();
184
185 record.push(
186 "installed_plugins",
187 Value::string(installed_plugins.join(", "), span),
188 );
189 }
190
191 record.push(
192 "experimental_options",
193 Value::string(
194 nu_experimental::ALL
195 .iter()
196 .map(|option| format!("{}={}", option.identifier(), option.get()))
197 .join(", "),
198 span,
199 ),
200 );
201
202 Ok(Value::record(record, span).into_pipeline_data())
203}
204
205fn push_version_numbers(record: &mut Record, head: Span) {
207 static VERSION_NUMBERS: OnceLock<(u8, u8, u8)> = OnceLock::new();
208
209 let &(major, minor, patch) = VERSION_NUMBERS.get_or_init(|| {
210 (
211 build::PKG_VERSION_MAJOR.parse().expect("Always set"),
212 build::PKG_VERSION_MINOR.parse().expect("Always set"),
213 build::PKG_VERSION_PATCH.parse().expect("Always set"),
214 )
215 });
216 record.push("major", Value::int(major.into(), head));
217 record.push("minor", Value::int(minor.into(), head));
218 record.push("patch", Value::int(patch.into(), head));
219}
220
221fn global_allocator() -> &'static str {
222 "standard"
223}
224
225#[cfg(test)]
226mod test {
227 #[test]
228 fn test_examples() {
229 use super::Version;
230 use crate::test_examples;
231 test_examples(Version)
232 }
233}