Streamline CI Cargo Checks (#17954)

This commit is contained in:
Piepmatz authored and GitHub committed 2026-04-02 15:25:22 +02:00
1 parent 0c7241cc71
commit 664e699d7c
49 files changed
+451 -247

No files matched your search

+141 -128
View File
@@ -8,73 +8,164 @@ on:
name: continuous-integration
env:
NUSHELL_CARGO_PROFILE: ci
NU_LOG_LEVEL: DEBUG
# If changing these settings also change toolkit.nu
CLIPPY_OPTIONS: "-D warnings -D clippy::unwrap_used -D clippy::unchecked_time_subtraction"
CLIPPY_OPTIONS: "-D warnings"
NUSHELL_CARGO_PROFILE: ci
MAIN_OPTIONS: --workspace --exclude nu_plugin_* --all-targets
PLUGIN_OPTIONS: --package nu_plugin_* --all-targets
WASM_OPTIONS: >
--no-default-features
--target wasm32-unknown-unknown
-p nu-cmd-base
-p nu-cmd-extra
-p nu-cmd-lang
-p nu-color-config
-p nu-command
-p nu-derive-value
-p nu-engine
-p nu-glob
-p nu-json
-p nu-parser
-p nu-path
-p nu-pretty-hex
-p nu-protocol
-p nu-std
-p nu-system
-p nu-table
-p nu-term-grid
-p nu-utils
-p nuon
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
fmt-clippy:
cargo:
name: "`cargo` in ${{ matrix.workspace.name }} (${{ matrix.target.name }})"
strategy:
fail-fast: true
matrix:
# Pinning to Ubuntu 22.04 because building on newer Ubuntu versions causes linux-gnu
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
# revisiting this when 22.04 is closer to EOL (June 2027)
platform: [windows-latest, macos-latest, ubuntu-22.04]
target:
# Pinning to Ubuntu 22.04 because building on newer Ubuntu versions causes linux-gnu
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
# revisiting this when 22.04 is closer to EOL (June 2027)
- name: Ubuntu
host: ubuntu-22.04
target: x86_64-unknown-linux-gnu
options: $MAIN_OPTIONS
steps: [fmt, clippy, build, test]
- name: Windows
host: windows-latest
target: x86_64-pc-windows-msvc
options: $MAIN_OPTIONS
steps: [fmt, clippy, build, test]
- name: MacOS
host: macos-latest
target: x86_64-apple-darwin
options: $MAIN_OPTIONS
steps: [fmt, clippy, build, test]
- name: Ubuntu Plugins
host: ubuntu-22.04
target: x86_64-unknown-linux-gnu
options: $PLUGIN_OPTIONS
steps: [clippy, build, test]
- name: Windows Plugins
host: windows-latest
target: x86_64-pc-windows-msvc
options: $PLUGIN_OPTIONS
steps: [clippy, build, test]
- name: MacOS Plugins
host: macos-latest
target: x86_64-apple-darwin
options: $PLUGIN_OPTIONS
steps: [clippy, build, test]
- name: WASM
host: ubuntu-22.04
target: wasm32-unknown-unknown
options: $WASM_OPTIONS
steps: [build, check]
runs-on: ${{ matrix.platform }}
workspace:
- name: root
path: ./
profile: ci
skip: []
- name: nu-parser/fuzz
path: ./crates/nu-parser/fuzz
profile: dev
skip: [build, test]
- name: nu-path/fuzz
path: ./crates/nu-path/fuzz
profile: dev
skip: [build, test]
exclude:
- target: { name: WASM }
workspace: { name: nu-parser/fuzz }
- target: { name: WASM }
workspace: { name: nu-path/fuzz }
- target: { name: Ubuntu Plugins }
workspace: { name: nu-parser/fuzz }
- target: { name: Ubuntu Plugins }
workspace: { name: nu-path/fuzz }
- target: { name: Windows Plugins }
workspace: { name: nu-parser/fuzz }
- target: { name: Windows Plugins }
workspace: { name: nu-path/fuzz }
- target: { name: MacOS Plugins }
workspace: { name: nu-parser/fuzz }
- target: { name: MacOS Plugins }
workspace: { name: nu-path/fuzz }
runs-on: ${{ matrix.target.host }}
steps:
- uses: actions/checkout@v6
- name: Checkout Repo
uses: actions/checkout@v6
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
- name: Setup Rust Toolchain
run: rustup target add ${{ matrix.target.target }}
working-directory: ${{ matrix.workspace.path }}
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: ${{ matrix.workspace.path }} -> target
cache-all-crates: true
cache-workspace-crates: true
- name: cargo fmt
run: cargo fmt --all -- --check
# If changing these settings also change toolkit.nu
- name: Clippy
run: cargo clippy --workspace --exclude nu_plugin_* -- $CLIPPY_OPTIONS
# In tests we don't have to deny unwrap
- name: Clippy of tests
run: cargo clippy --tests --workspace --exclude nu_plugin_* -- -D warnings
- name: Clippy of benchmarks
run: cargo clippy --benches --workspace --exclude nu_plugin_* -- -D warnings
tests:
strategy:
fail-fast: true
matrix:
platform: [windows-latest, macos-latest, ubuntu-22.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v6
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
- name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_*
- name: Check for clean repo
shell: bash
if: contains(matrix.target.steps, 'fmt') && !contains(matrix.workspace.skip, 'fmt')
working-directory: ${{ matrix.workspace.path }}
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi
rustup component add rustfmt
cargo fmt --all --check
- name: cargo check
if: contains(matrix.target.steps, 'check') && !contains(matrix.workspace.skip, 'check')
working-directory: ${{ matrix.workspace.path }}
run: cargo check ${{ matrix.target.options }} --profile ${{ matrix.workspace.profile }}
- name: cargo clippy
if: contains(matrix.target.steps, 'clippy') && !contains(matrix.workspace.skip, 'clippy')
working-directory: ${{ matrix.workspace.path }}
run: |
rustup component add clippy
cargo clippy ${{ matrix.target.options }} --profile ${{ matrix.workspace.profile }} -- $CLIPPY_OPTIONS
- name: cargo build
if: contains(matrix.target.steps, 'build') && !contains(matrix.workspace.skip, 'build')
working-directory: ${{ matrix.workspace.path }}
run: cargo build ${{ matrix.target.options }} --profile ${{ matrix.workspace.profile }}
- name: cargo test
if: contains(matrix.target.steps, 'test') && !contains(matrix.workspace.skip, 'test')
working-directory: ${{ matrix.workspace.path }}
run: cargo test ${{ matrix.target.options }} --profile ${{ matrix.workspace.profile }}
- name: Assert Clean Repo
run: git diff --quiet && git diff --cached --quiet
std-lib-and-python-virtualenv:
strategy:
@@ -140,81 +231,3 @@ jobs:
else
echo "no changes in working directory";
fi
plugins:
strategy:
fail-fast: true
matrix:
platform: [windows-latest, macos-latest, ubuntu-22.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v6
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
- name: Clippy
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
- name: Tests
run: cargo test --profile ci --package nu_plugin_*
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi
wasm:
env:
WASM_OPTIONS: --no-default-features --target wasm32-unknown-unknown
CLIPPY_CONF_DIR: ${{ github.workspace }}/clippy/wasm/
strategy:
matrix:
job:
- name: Build WASM
command: cargo build
args:
- name: Clippy WASM
command: cargo clippy
args: -- $CLIPPY_OPTIONS
name: ${{ matrix.job.name }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
- name: Add wasm32-unknown-unknown target
run: rustup target add wasm32-unknown-unknown
- run: ${{ matrix.job.command }} -p nu-cmd-base $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-cmd-extra $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-cmd-lang $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-color-config $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-command $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-derive-value $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-engine $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-glob $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-json $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-parser $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-path $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-pretty-hex $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-protocol $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-std $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-system $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-table $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-term-grid $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nu-utils $WASM_OPTIONS ${{ matrix.job.args }}
- run: ${{ matrix.job.command }} -p nuon $WASM_OPTIONS ${{ matrix.job.args }}
Generated
+3
View File
@@ -4542,6 +4542,7 @@ dependencies = [
"log",
"nu-plugin-protocol",
"nu-protocol",
"nu-utils",
"rmp-serde",
"serde",
"serde_json",
@@ -4668,6 +4669,7 @@ dependencies = [
"mach2",
"nix 0.30.1",
"ntapi",
"nu-utils",
"procfs",
"sysinfo",
"uucore",
@@ -4759,6 +4761,7 @@ dependencies = [
"strip-ansi-escapes",
"sys-locale",
"unicase",
"web-time",
]
[[package]]
+1
View File
@@ -240,6 +240,7 @@ format_push_string = "warn"
needless_raw_strings = "warn"
result_large_err = "allow"
unchecked_time_subtraction = "warn"
unwrap_used = "deny"
used_underscore_binding = "warn"
[lints]
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use nu_cli::{eval_source, evaluate_commands};
use nu_plugin_core::{Encoder, EncodingType};
use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
+6
View File
@@ -0,0 +1,6 @@
allow-unwrap-in-tests = true
[[disallowed-types]]
path = "std::time::Instant"
reason = "WASM panics if used, use instead"
replacement = "nu_utils::time::Instant"
-3
View File
@@ -1,3 +0,0 @@
[[disallowed-types]]
path = "std::time::Instant"
reason = "WASM panics if used, use `web_time::Instant` instead"
+6 -4
View File
@@ -12,6 +12,8 @@ use nu_protocol::{
};
#[cfg(feature = "plugin")]
use nu_utils::perf;
#[cfg(feature = "plugin")]
use nu_utils::time::Instant;
use std::path::PathBuf;
#[cfg(feature = "plugin")]
@@ -48,7 +50,7 @@ pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Span
return;
}
let mut start_time = std::time::Instant::now();
let mut start_time = Instant::now();
// Reading signatures from plugin registry file
// The plugin.msgpackz file stores the parsed signature collected from each registered plugin
add_plugin_file(engine_state, plugin_file.clone());
@@ -61,7 +63,7 @@ pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Span
.get(engine_state)
);
start_time = std::time::Instant::now();
start_time = Instant::now();
let plugin_path = engine_state.plugin_path.clone();
if let Some(plugin_path) = plugin_path {
// Open the plugin file
@@ -140,7 +142,7 @@ pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Span
.use_ansi_coloring
.get(engine_state)
);
start_time = std::time::Instant::now();
start_time = Instant::now();
let mut working_set = StateWorkingSet::new(engine_state);
@@ -268,7 +270,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
};
use std::collections::BTreeMap;
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let Ok(cwd) = engine_state.cwd_as_string(None) else {
return false;
+2
View File
@@ -1,4 +1,6 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(test, allow(clippy::unwrap_used))]
mod commands;
mod completions;
mod config_files;
+19 -18
View File
@@ -31,6 +31,7 @@ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
report_shell_error,
};
use nu_utils::time::Instant;
use nu_utils::{
filesystem::{PermissionResult, have_permission},
perf,
@@ -50,7 +51,7 @@ use std::{
panic::{AssertUnwindSafe, catch_unwind},
path::{Path, PathBuf},
sync::Arc,
time::{Duration, Instant},
time::Duration,
};
use sysinfo::System;
@@ -288,7 +289,7 @@ fn escape_special_vscode_bytes(input: &str) -> Result<String, ShellError> {
}
fn get_line_editor(engine_state: &mut EngineState, use_color: bool) -> Result<Reedline> {
let mut start_time = std::time::Instant::now();
let mut start_time = Instant::now();
let mut line_editor = Reedline::create();
// Now that reedline is created, get the history session id and store it in engine_state
@@ -296,7 +297,7 @@ fn get_line_editor(engine_state: &mut EngineState, use_color: bool) -> Result<Re
perf!("setup reedline", start_time, use_color);
if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now();
start_time = Instant::now();
line_editor = setup_history(engine_state, line_editor, history)?;
@@ -322,7 +323,7 @@ struct LoopContext<'a> {
fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
use nu_cmd_base::hook;
use reedline::Signal;
let loop_start_time = std::time::Instant::now();
let loop_start_time = Instant::now();
let LoopContext {
engine_state,
@@ -335,7 +336,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
hostname,
} = ctx;
let mut start_time = std::time::Instant::now();
let mut start_time = Instant::now();
// Before doing anything, merge the environment from the previous REPL iteration into the
// permanent state.
if let Err(err) = engine_state.merge_env(&mut stack) {
@@ -343,11 +344,11 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
}
perf!("merge env", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
engine_state.reset_signals();
perf!("reset signals", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
// Check all the environment variables they ask for
// fire the "env_change" hook
if let Err(error) = hook::eval_env_change_hook(
@@ -359,7 +360,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
}
perf!("env-change hook", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
// Next, right before we start our prompt and take input from the user, fire the "pre_prompt" hook
if let Err(err) = hook::eval_hooks(
engine_state,
@@ -375,7 +376,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
let engine_reference = Arc::new(engine_state.clone());
let config = stack.get_config(engine_state);
start_time = std::time::Instant::now();
start_time = Instant::now();
// Find the configured cursor shapes for each mode
let cursor_config = CursorConfig {
vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape.vi_insert),
@@ -384,7 +385,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
};
perf!("get config/cursor config", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
// at this line we have cloned the state for the completer and the transient prompt
// until we drop those, we cannot use the stack in the REPL loop itself
// See STACK-REFERENCE to see where we have taken a reference
@@ -441,7 +442,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
let style_computer = StyleComputer::from_config(engine_state, &stack_arc);
start_time = std::time::Instant::now();
start_time = Instant::now();
line_editor = if config.use_ansi_coloring.get(engine_state) && config.show_hints {
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
// No meaningful span — this is a synthetic null value for style computation.
@@ -462,7 +463,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("reedline coloring/style_computer", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
trace!("adding menus");
line_editor =
add_menus(line_editor, engine_reference, &stack_arc, config).unwrap_or_else(|e| {
@@ -472,7 +473,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("reedline adding menus", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
// No call span available in the REPL loop for editor lookup
let buffer_editor = get_editor(engine_state, &stack_arc, Span::unknown());
@@ -491,7 +492,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("reedline buffer_editor", start_time, use_color);
if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now();
start_time = Instant::now();
line_editor = line_editor
.with_history_exclusion_prefix(history.ignore_space_prefixed.then_some(" ".into()));
@@ -505,13 +506,13 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("sync_history", start_time, use_color);
}
start_time = std::time::Instant::now();
start_time = Instant::now();
// Changing the line editor based on the found keybindings
line_editor = setup_keybindings(engine_state, line_editor);
perf!("keybindings", start_time, use_color);
start_time = std::time::Instant::now();
start_time = Instant::now();
let config = &engine_state.get_config().clone();
prompt_update::update_prompt(
config,
@@ -530,7 +531,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
*entry_num += 1;
start_time = std::time::Instant::now();
start_time = Instant::now();
line_editor = line_editor.with_transient_prompt(transient_prompt);
let input = line_editor.read_line(nu_prompt);
// we got our inputs, we can now drop our stack references
@@ -557,7 +558,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("line_editor setup", start_time, use_color);
let line_editor_input_time = std::time::Instant::now();
let line_editor_input_time = Instant::now();
match input {
Ok(Signal::Success(repl_cmd_line_text)) => {
let history_supports_meta = match engine_state.history_config().map(|h| h.file_format) {
+2 -1
View File
@@ -14,6 +14,7 @@ use nu_protocol::{
};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
use nu_utils::time::Instant;
use nu_utils::{escape_quote_string, perf};
use std::path::Path;
@@ -243,7 +244,7 @@ pub fn eval_source(
input: PipelineData,
allow_return: bool,
) -> i32 {
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let exit_code = match evaluate_source(engine_state, stack, source, fname, input, allow_return) {
Ok(failed) => {
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
mod commands;
mod completions;
+1 -1
View File
@@ -1,6 +1,6 @@
use nu_engine::{ClosureEvalOnce, command_prelude::*};
use nu_protocol::engine::Closure;
use web_time::Instant;
use nu_utils::time::Instant;
#[derive(Clone)]
pub struct TimeIt;
@@ -1,8 +1,10 @@
use std::{
sync::mpsc::{RecvTimeoutError, TryRecvError},
time::{Duration, Instant},
time::Duration,
};
use nu_utils::time::Instant;
use nu_engine::command_prelude::*;
use nu_protocol::{
+3 -1
View File
@@ -12,9 +12,11 @@ use std::{
io::{self, BufRead, BufReader, Read, Write},
path::{Path, PathBuf},
thread,
time::{Duration, Instant},
time::Duration,
};
use nu_utils::time::Instant;
#[derive(Clone)]
pub struct Save;
@@ -333,10 +333,11 @@ pub fn make_on_connect(handlers: &nu_protocol::Handlers) -> OnConnect {
mod tests {
use super::*;
use nu_protocol::{Handlers, SignalAction};
use nu_utils::time::Instant;
use std::io::Write;
use std::net::TcpListener;
use std::thread;
use std::time::{Duration, Instant};
use std::time::Duration;
#[test]
fn test_interrupt_unblocks_read() {
@@ -252,9 +252,10 @@ mod tests {
#[test]
fn test_interrupt_unblocks_read() {
use nu_protocol::{Handlers, SignalAction};
use nu_utils::time::Instant;
use std::io::Write;
use std::thread;
use std::time::{Duration, Instant};
use std::time::Duration;
#[cfg(unix)]
use std::os::unix::net::UnixListener;
@@ -4,7 +4,9 @@ use crossterm::event::{
};
use crossterm::{execute, terminal};
use nu_engine::command_prelude::*;
use std::time::{Duration, Instant};
use std::time::Duration;
use nu_utils::time::Instant;
use nu_protocol::shell_error::{generic::GenericError, io::IoError};
use num_traits::AsPrimitive;
+4 -5
View File
@@ -1,9 +1,8 @@
use nu_engine::command_prelude::*;
use std::{
thread,
time::{Duration, Instant},
};
use std::{thread, time::Duration};
use nu_utils::time::Instant;
const CTRL_C_CHECK_INTERVAL: Duration = Duration::from_millis(100);
@@ -93,7 +92,7 @@ mod tests {
#[test]
fn examples_work_as_expected() -> nu_test_support::Result {
use std::time::Instant;
use nu_utils::time::Instant;
let start = Instant::now();
nu_test_support::test().examples(Sleep)?;
+28 -23
View File
@@ -277,29 +277,6 @@ fn reg_value_to_nu_string(
}
}
#[test]
fn no_expand_does_not_expand() {
let unexpanded = "%AppData%";
let reg_val = || winreg::RegValue {
bytes: unexpanded
.encode_utf16()
.chain([0])
.flat_map(u16::to_ne_bytes)
.collect(),
vtype: REG_EXPAND_SZ,
};
// normally we do expand
let nu_val_expanded = reg_value_to_nu_string(reg_val(), Span::unknown(), false);
assert!(nu_val_expanded.coerce_string().is_ok());
assert_ne!(nu_val_expanded.coerce_string().unwrap(), unexpanded);
// unless we skip expansion
let nu_val_skip_expand = reg_value_to_nu_string(reg_val(), Span::unknown(), true);
assert!(nu_val_skip_expand.coerce_string().is_ok());
assert_eq!(nu_val_skip_expand.coerce_string().unwrap(), unexpanded);
}
fn reg_value_to_nu_list_string(reg_value: winreg::RegValue, call_span: Span) -> nu_protocol::Value {
let values = <Vec<String>>::from_reg_value(&reg_value)
.expect("registry value type should be REG_MULTI_SZ")
@@ -329,3 +306,31 @@ fn reg_value_to_nu_int(reg_value: winreg::RegValue, call_span: Span) -> nu_proto
};
Value::int(value, call_span)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_expand_does_not_expand() {
let unexpanded = "%AppData%";
let reg_val = || winreg::RegValue {
bytes: unexpanded
.encode_utf16()
.chain([0])
.flat_map(u16::to_ne_bytes)
.collect(),
vtype: REG_EXPAND_SZ,
};
// normally we do expand
let nu_val_expanded = reg_value_to_nu_string(reg_val(), Span::unknown(), false);
assert!(nu_val_expanded.coerce_string().is_ok());
assert_ne!(nu_val_expanded.coerce_string().unwrap(), unexpanded);
// unless we skip expansion
let nu_val_skip_expand = reg_value_to_nu_string(reg_val(), Span::unknown(), true);
assert!(nu_val_skip_expand.coerce_string().is_ok());
assert_eq!(nu_val_skip_expand.coerce_string().unwrap(), unexpanded);
}
}
+1 -1
View File
@@ -7,8 +7,8 @@ use std::{collections::VecDeque, io::Read, path::PathBuf, str::FromStr, time::Du
use devicons::icon_for_file;
use lscolors::{LsColors, Style};
use nu_color_config::lookup_ansi_color_style;
use nu_utils::time::Instant;
use url::Url;
use web_time::Instant;
use nu_color_config::{StyleComputer, TextStyle, color_from_hex};
use nu_engine::{command_prelude::*, env_to_string};
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
mod commands;
mod format_conversions;
@@ -1,7 +1,6 @@
use std::{
io::Result,
time::{Duration, Instant},
};
use std::{io::Result, time::Duration};
use nu_utils::time::Instant;
use crossterm::event::{Event, KeyEvent, KeyEventKind, poll, read};
+2 -1
View File
@@ -15,6 +15,7 @@ use nu_protocol::{
Span,
engine::{EngineState, StateWorkingSet},
};
use nu_utils::time::Instant;
use std::{
collections::{BTreeMap, HashMap, HashSet},
fs,
@@ -225,7 +226,7 @@ impl LanguageServer {
.ok();
// TODO: WorkDoneProgress -> PartialResults for quicker response
// currently not enabled by `lsp_types` but hackable in `server_capabilities` json
let time_start = std::time::Instant::now();
let time_start = Instant::now();
loop {
if self.handle_internal_messages().ok()? {
break;
+1 -1
View File
@@ -9,5 +9,5 @@ fuzz_target!(|data: &[u8]| {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let _block = parse(&mut working_set, None, &data, true);
let _block = parse(&mut working_set, None, data, true);
});
@@ -10,5 +10,5 @@ fuzz_target!(|data: &[u8]| {
let engine_state = create_default_context();
let mut working_set = StateWorkingSet::new(&engine_state);
let _block = parse(&mut working_set, None, &data, true);
let _block = parse(&mut working_set, None, data, true);
});
@@ -17,6 +17,6 @@ fuzz_target!(|data: &[u8]| {
// Here, we're assuming a second path for the "relative to" aspect.
// For simplicity, we're just using the current directory.
let current_dir = std::path::Path::new(".");
let _ = expand_path_with(path, &current_dir, true);
let _ = expand_path_with(path, current_dir, true);
}
});
+2 -1
View File
@@ -15,8 +15,9 @@ bench = false
workspace = true
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.111.1" }
nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.111.1", default-features = false }
nu-protocol = { path = "../nu-protocol", version = "0.111.1" }
nu-utils = { path = "../nu-utils", version = "0.111.1" }
rmp-serde = { workspace = true }
serde = { workspace = true }
@@ -173,7 +173,8 @@ impl PreparedServerCommunication {
PreparedServerCommunication::LocalSocket { listener, .. } => {
use interprocess::local_socket::ListenerNonblockingMode;
use interprocess::local_socket::traits::{Listener, Stream};
use std::time::{Duration, Instant};
use nu_utils::time::Instant;
use std::time::Duration;
const RETRY_PERIOD: Duration = Duration::from_millis(1);
const TIMEOUT: Duration = Duration::from_secs(10);
@@ -4,9 +4,11 @@ use std::{
atomic::{AtomicBool, Ordering::Relaxed},
mpsc,
},
time::{Duration, Instant},
time::Duration,
};
use nu_utils::time::Instant;
use super::{StreamManager, StreamReader, StreamWriter, StreamWriterSignal, WriteStreamMessage};
use nu_plugin_protocol::{StreamData, StreamMessage};
use nu_protocol::{ShellError, Value};
+3 -1
View File
@@ -3,9 +3,11 @@ use nu_protocol::{PluginGcConfig, RegisteredPlugin};
use std::{
sync::{Arc, Weak, mpsc},
thread,
time::{Duration, Instant},
time::Duration,
};
use nu_utils::time::Instant;
/// Plugin garbage collector
///
/// Many users don't want all of their plugins to stay running indefinitely after using them, so
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use nu_pretty_hex::*;
// #[test]
+1 -1
View File
@@ -12,11 +12,11 @@ use crate::{
record,
shell_error::generic::GenericError,
};
use nu_utils::time::Instant;
use std::{
borrow::{Borrow, Cow},
io::BufRead,
};
use web_time::Instant;
#[derive(Debug, Clone, Copy)]
struct ElementId(usize);
+3 -1
View File
@@ -7,7 +7,9 @@ use std::{
};
#[cfg(not(target_family = "wasm"))]
use std::time::{Duration, Instant};
use nu_utils::time::Instant;
#[cfg(not(target_family = "wasm"))]
use std::time::Duration;
use nu_system::{UnfreezeHandle, kill_by_pid};
@@ -32,12 +32,18 @@ impl From<ShellErrorBridge> for std::io::Error {
}
}
#[test]
fn test_bridge_io_error_roundtrip() {
let shell_error = ShellError::Generic(GenericError::new_internal("some error", "some message"));
#[cfg(test)]
mod tests {
use super::*;
let bridge = ShellErrorBridge(shell_error);
let io_error = std::io::Error::from(bridge.clone());
let bridge_again = ShellErrorBridge::try_from(io_error).unwrap();
assert_eq!(bridge.0, bridge_again.0);
#[test]
fn test_bridge_io_error_roundtrip() {
let shell_error =
ShellError::Generic(GenericError::new_internal("some error", "some message"));
let bridge = ShellErrorBridge(shell_error);
let io_error = std::io::Error::from(bridge.clone());
let bridge_again = ShellErrorBridge::try_from(io_error).unwrap();
assert_eq!(bridge.0, bridge_again.0);
}
}
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
mod into_config;
mod pipeline;
mod test_config;
+2
View File
@@ -17,6 +17,8 @@ bench = false
workspace = true
[dependencies]
nu-utils = { path = "../nu-utils", version = "0.111.1", default-features = false }
libc = { workspace = true }
log = { workspace = true }
sysinfo = { workspace = true }
+3 -1
View File
@@ -8,9 +8,11 @@ use std::{
io,
mem::{self, MaybeUninit},
ptr,
time::{Duration, Instant},
time::Duration,
};
use nu_utils::time::Instant;
#[derive(Debug)]
pub struct ProcessInfo {
pub pid: i32,
+2 -1
View File
@@ -1,9 +1,10 @@
use log::info;
use nu_utils::time::Instant;
use procfs::process::{FDInfo, Io, Process, Stat, Status};
use procfs::{ProcError, ProcessCGroups, WithCurrentSystemInfo};
use std::path::PathBuf;
use std::thread;
use std::time::{Duration, Instant};
use std::time::Duration;
pub enum ProcessTask {
Process(Process),
+2 -1
View File
@@ -8,10 +8,11 @@ use libproc::libproc::task_info::{TaskAllInfo, TaskInfo};
use libproc::libproc::thread_info::ThreadInfo;
use libproc::processes::{ProcFilter, pids_by_type};
use mach2::mach_time;
use nu_utils::time::Instant;
use std::cmp;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::{Duration, Instant};
use std::time::Duration;
pub struct ProcessInfo {
pub pid: i32,
+3 -1
View File
@@ -6,9 +6,11 @@ use std::{
io,
mem::{self, MaybeUninit},
ptr,
time::{Duration, Instant},
time::Duration,
};
use nu_utils::time::Instant;
#[cfg(target_os = "netbsd")]
type KInfoProc = libc::kinfo_proc2;
#[cfg(target_os = "openbsd")]
+1 -1
View File
@@ -8,6 +8,7 @@ use libc::c_void;
use ntapi::ntrtl::RTL_USER_PROCESS_PARAMETERS;
use ntapi::ntwow64::{PEB32, RTL_USER_PROCESS_PARAMETERS32};
use nu_utils::time::Instant;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::OsString;
@@ -19,7 +20,6 @@ use std::ptr::null_mut;
use std::sync::LazyLock;
use std::thread;
use std::time::Duration;
use web_time::Instant;
use windows::core::{PCWSTR, PWSTR};
+3
View File
@@ -43,5 +43,8 @@ crossterm_winapi = "0.9"
[target.'cfg(unix)'.dependencies]
nix = { workspace = true, default-features = false, features = ["user", "fs"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-time = { workspace = true }
[lints]
workspace = true
+1
View File
@@ -20,6 +20,7 @@ mod shared_cow;
mod split_read;
pub mod strings;
pub mod sync;
pub mod time;
pub mod utils;
pub use locale::get_system_locale;
+129
View File
@@ -0,0 +1,129 @@
#![allow(
clippy::disallowed_types,
reason = "only allow std::time::Instant here when it's not WASM"
)]
#![allow(clippy::unchecked_time_subtraction, reason = "just forwarded")]
use std::{
ops::{Add, AddAssign, Deref, Sub, SubAssign},
time::Duration,
};
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant as InnerInstant;
#[cfg(target_arch = "wasm32")]
pub use web_time::Instant as InnerInstant;
/// WASM-safe alternative to [`std::time::Instant`].
///
/// This is a thin wrapper around either `std::time::Instant` or `web_time::Instant` to allow
/// compiling for WASM without issues.
/// `web_time::Instant` usually re-exports `std::time::Instant` on non-WASM targets but this does
/// not allow usage of `clippy::disallowed-types` properly.
/// This wrapper fixes that.
///
/// For any reference, see [`std::time::Instant`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(InnerInstant);
impl Instant {
/// Returns an instant corresponding to "now".
///
/// See [`Instant::now`](InnerInstant::now).
pub fn now() -> Self {
Instant(InnerInstant::now())
}
/// Returns the amount of time elapsed from another instant to this one, or zero duration if
/// that instant is later than this one.
///
/// See [`Instant::duration_since`](InnerInstant::duration_since).
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.0.duration_since(earlier.0)
}
/// Returns the amount of time elapsed from another instant to this one, or None if that instant
/// is later than this one.
///
/// See [`Instant::checked_duration_since`](InnerInstant::checked_duration_since).
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
self.0.checked_duration_since(earlier.0)
}
/// Returns the amount of time elapsed from another instant to this one, or zero duration if
/// that instant is later than this one.
///
/// See [`Instant::saturating_duration_since`](InnerInstant::saturating_duration_since).
pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
self.0.saturating_duration_since(earlier.0)
}
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// See [`Instant::checked_add`](InnerInstant::checked_add).
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
self.0.checked_add(duration).map(Self)
}
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// See [`Instant::checked_sub`](InnerInstant::checked_sub).
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
self.0.checked_sub(duration).map(Self)
}
}
impl Deref for Instant {
type Target = InnerInstant;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Add<Duration> for Instant {
type Output = Instant;
fn add(self, rhs: Duration) -> Self::Output {
Instant(self.0.add(rhs))
}
}
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, rhs: Duration) {
self.0.add_assign(rhs)
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, rhs: Duration) -> Self::Output {
Instant(self.0.sub(rhs))
}
}
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, rhs: Duration) {
self.0.sub_assign(rhs)
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, rhs: Instant) -> Self::Output {
self.0.sub(rhs.0)
}
}
impl From<InnerInstant> for Instant {
fn from(value: InnerInstant) -> Self {
Self(value)
}
}
@@ -6,6 +6,7 @@ use crate::{
};
use log::debug;
use nu_utils::perf;
use nu_utils::time::Instant;
use nu_plugin::{EvaluatedCall, PluginCommand};
use nu_protocol::{
@@ -447,7 +448,7 @@ fn from_ndjson(
);
let maybe_schema = get_schema(plugin, call)?;
if !is_eager {
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let df = LazyJsonLineReader::new(resource.path)
.with_infer_schema_length(Some(infer_schema))
@@ -486,7 +487,7 @@ fn from_ndjson(
None => reader,
};
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let df: NuDataFrame = reader
.finish()
@@ -564,7 +565,7 @@ fn from_csv(
Some(r) => csv_reader.with_skip_rows(r),
};
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let df: NuLazyFrame = csv_reader
.finish()
.map_err(|e| {
@@ -581,7 +582,7 @@ fn from_csv(
df.cache_and_to_value(plugin, engine, call.head)
} else {
let file_span = resource.span;
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let df = CsvReadOptions::default()
.with_has_header(!no_header)
.with_infer_schema_length(Some(infer_schema))
+13 -13
View File
@@ -72,8 +72,8 @@ fn current_dir_from_environment() -> PathBuf {
}
fn main() -> Result<()> {
let entire_start_time = std::time::Instant::now();
let mut start_time = std::time::Instant::now();
let entire_start_time = nu_utils::time::Instant::now();
let mut start_time = nu_utils::time::Instant::now();
miette::set_panic_hook();
let miette_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |x| {
@@ -293,7 +293,7 @@ fn main() -> Result<()> {
perf!("start logging", start_time, use_color);
}
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
set_config_path(
&mut engine_state,
init_cwd.as_ref(),
@@ -313,12 +313,12 @@ fn main() -> Result<()> {
#[cfg(unix)]
{
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
terminal::acquire(engine_state.is_interactive);
perf!("acquire_terminal", start_time, use_color);
}
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
// No source span — default config is synthesized at startup
engine_state.add_env_var(
"config".into(),
@@ -331,13 +331,13 @@ fn main() -> Result<()> {
Value::test_record(record! {}),
);
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
// First, set up env vars as strings only
gather_parent_env_vars(&mut engine_state, init_cwd.as_ref());
perf!("gather env vars", start_time, use_color);
let mut stack = Stack::new();
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
let config = engine_state.get_config();
let use_color = config.use_ansi_coloring.get(&engine_state);
// Translate environment variables from Strings to Values
@@ -347,7 +347,7 @@ fn main() -> Result<()> {
perf!("Convert path to list", start_time, use_color);
// Set up NU_LIB_DIRS: constant = defaults + env + -I, env = env + -I
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
{
/// Parse a string into a list of paths, splitting on the given separators.
fn parse_path_list(value: &str, separators: &[char]) -> Vec<String> {
@@ -457,7 +457,7 @@ fn main() -> Result<()> {
return Ok(());
}
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
if let Some(testbin) = &parsed_nu_cli_args.testbin {
let dispatcher = test_bins::new_testbin_dispatcher();
let test_bin = testbin.item.as_str();
@@ -482,7 +482,7 @@ fn main() -> Result<()> {
}
perf!("run test_bins", start_time, use_color);
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
let input = if let Some(redirect_stdin) = &parsed_nu_cli_args.redirect_stdin {
trace!("redirecting stdin");
PipelineData::byte_stream(ByteStream::stdin(redirect_stdin.span)?, None)
@@ -492,7 +492,7 @@ fn main() -> Result<()> {
};
perf!("redirect stdin", start_time, use_color);
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
// Set up the $nu constant before evaluating config files (need to have $nu available in them)
engine_state.generate_nu_constant();
perf!("create_nu_constant", start_time, use_color);
@@ -503,7 +503,7 @@ fn main() -> Result<()> {
use nu_protocol::{ErrSpan, PluginIdentity, RegisteredPlugin, engine::StateWorkingSet};
// Load any plugins specified with --plugins
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
let mut working_set = StateWorkingSet::new(&engine_state);
for plugin_filename in plugins {
@@ -542,7 +542,7 @@ fn main() -> Result<()> {
perf!("load plugins specified in --plugins", start_time, use_color)
}
start_time = std::time::Instant::now();
start_time = nu_utils::time::Instant::now();
#[cfg(feature = "mcp")]
if parsed_nu_cli_args.mcp {
+14 -13
View File
@@ -12,6 +12,7 @@ use nu_protocol::{
report_shell_error,
};
use nu_utils::perf;
use nu_utils::time::Instant;
pub(crate) fn run_commands(
engine_state: &mut EngineState,
@@ -20,11 +21,11 @@ pub(crate) fn run_commands(
use_color: bool,
commands: &Spanned<String>,
input: PipelineData,
entire_start_time: std::time::Instant,
entire_start_time: nu_utils::time::Instant,
) {
trace!("run_commands");
let start_time = std::time::Instant::now();
let start_time = nu_utils::time::Instant::now();
let create_scaffold = nu_path::nu_config_dir().is_some_and(|p| !p.exists());
// if the --no-config-file(-n) option is NOT passed, load the plugin file,
@@ -38,7 +39,7 @@ pub(crate) fn run_commands(
perf!("read plugins", start_time, use_color);
let start_time = std::time::Instant::now();
let start_time = Instant::now();
// If we have a env file parameter *OR* we have a login shell parameter, read the env file
if parsed_nu_cli_args.env_file.is_some() || parsed_nu_cli_args.login_shell.is_some() {
config_files::read_config_file(
@@ -55,7 +56,7 @@ pub(crate) fn run_commands(
perf!("read env.nu", start_time, use_color);
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let create_scaffold = nu_path::nu_config_dir().is_some_and(|p| !p.exists());
// If we have a config file parameter *OR* we have a login shell parameter, read the config file
@@ -73,7 +74,7 @@ pub(crate) fn run_commands(
perf!("read config.nu", start_time, use_color);
// If we have a login shell parameter, read the login file
let start_time = std::time::Instant::now();
let start_time = Instant::now();
if parsed_nu_cli_args.login_shell.is_some() {
config_files::read_loginshell_file(engine_state, &mut stack, false);
}
@@ -87,7 +88,7 @@ pub(crate) fn run_commands(
// Regenerate the $nu constant to contain the startup time and any other potential updates
engine_state.generate_nu_constant();
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let result = evaluate_commands(
commands,
engine_state,
@@ -124,13 +125,13 @@ pub(crate) fn run_file(
//
// if the --no-config-file(-n) flag is passed, do not load plugin, env, or config files
if parsed_nu_cli_args.no_config_file.is_none() {
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let create_scaffold = nu_path::nu_config_dir().is_some_and(|p| !p.exists());
#[cfg(feature = "plugin")]
read_plugin_file(engine_state, parsed_nu_cli_args.plugin_file);
perf!("read plugins", start_time, use_color);
let start_time = std::time::Instant::now();
let start_time = Instant::now();
// only want to load config and env if relative argument is provided.
if parsed_nu_cli_args.env_file.is_some() {
config_files::read_config_file(
@@ -146,7 +147,7 @@ pub(crate) fn run_file(
}
perf!("read env.nu", start_time, use_color);
let start_time = std::time::Instant::now();
let start_time = Instant::now();
if parsed_nu_cli_args.config_file.is_some() {
config_files::read_config_file(
engine_state,
@@ -163,7 +164,7 @@ pub(crate) fn run_file(
// Regenerate the $nu constant to contain the startup time and any other potential updates
engine_state.generate_nu_constant();
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let result = evaluate_file(
script_name,
&args_to_script,
@@ -183,10 +184,10 @@ pub(crate) fn run_repl(
engine_state: &mut EngineState,
mut stack: Stack,
parsed_nu_cli_args: command::NushellCliArgs,
entire_start_time: std::time::Instant,
entire_start_time: nu_utils::time::Instant,
) -> Result<(), miette::ErrReport> {
trace!("run_repl");
let start_time = std::time::Instant::now();
let start_time = nu_utils::time::Instant::now();
if parsed_nu_cli_args.no_config_file.is_none() {
setup_config(
@@ -207,7 +208,7 @@ pub(crate) fn run_repl(
.get(engine_state);
perf!("setup_config", start_time, use_color);
let start_time = std::time::Instant::now();
let start_time = Instant::now();
let ret_val = evaluate_repl(
engine_state,
stack,
+1
View File
@@ -1,4 +1,5 @@
#![allow(non_snake_case)]
#![allow(clippy::unwrap_used)]
mod const_;
mod eval;
-2
View File
@@ -47,8 +47,6 @@ export def "build wasm" [] {
export def "clippy wasm" [] {
prep wasm
$env.CLIPPY_CONF_DIR = $nushell_dir | path join clippy wasm
for crate in $wasm_compatible_crates {
print $'(char nl)Checking ($crate) for wasm'
print '----------------------------'