nu-command 0.112.1

Nushell's built-in commands
Documentation
use super::clipboard::provider::{Clipboard, create_clipboard};
use crate::{
    platform::clip::get_config::get_clip_config_with_plugin_fallback, try_json_str_to_value,
};
use nu_engine::command_prelude::*;
use nu_protocol::{Config, shell_error::generic::GenericError};

#[derive(Clone)]
pub struct ClipPaste;

impl Command for ClipPaste {
    fn name(&self) -> &str {
        "clip paste"
    }

    fn signature(&self) -> Signature {
        Signature::build(self.name())
            .switch(
                "raw",
                "Disable JSON parsing. (act inverted if default_raw config is true).",
                Some('r'),
            )
            .input_output_types(vec![(Type::Nothing, Type::Any)])
            .category(Category::System)
    }

    fn description(&self) -> &str {
        "Output the current clipboard content.
  By default, it tries to parse clipboard content as JSON and outputs the corresponding Nushell value.
  This behavior can be inverted using `$env.config.clip.default_raw = true`."
    }
    fn run(
        &self,
        engine_state: &EngineState,
        stack: &mut Stack,
        call: &Call,
        _input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        let config = stack.get_config(engine_state);
        let text = create_clipboard(&config, engine_state, stack).get_text()?;
        if text.trim().is_empty() {
            return Err(ShellError::Generic(GenericError::new(
                "Clipboard is empty.",
                "No text data is currently available in the clipboard.",
                call.head,
            )));
        }

        let default_raw = get_default_raw(&config, engine_state, stack);
        if default_raw != call.has_flag(engine_state, stack, "raw")? {
            return Ok(Value::string(text, call.head).into_pipeline_data());
        }

        let trimmed = text.trim_start();
        if trimmed.starts_with('{') || trimmed.starts_with('[') || trimmed.starts_with('"') {
            return match try_json_str_to_value(trimmed, call.head, false) {
                Ok(value) => Ok(value.into_pipeline_data()),
                Err(_) => Ok(Value::string(text, call.head).into_pipeline_data()),
            };
        }

        Ok(Value::string(text, call.head).into_pipeline_data())
    }

    fn examples(&self) -> Vec<Example<'_>> {
        vec![
            Example {
                example: "clip paste",
                description: "Paste from clipboard and try to parse JSON.",
                result: None,
            },
            Example {
                example: "clip paste --raw",
                description: "Paste raw clipboard text without JSON parsing.",
                result: None,
            },
        ]
    }
}

fn get_default_raw(config: &Config, engine_state: &EngineState, stack: &mut Stack) -> bool {
    // new config
    if config.clip.default_raw {
        return true;
    }
    // legacy config
    get_legacy_default_raw(get_clip_config_with_plugin_fallback(engine_state, stack).as_ref())
}

fn get_legacy_default_raw(value: Option<&Value>) -> bool {
    match value {
        Some(Value::Record { val, .. }) => {
            if let Some(Value::Bool { val, .. }) = val
                .get("DEFAULT_RAW")
                .or_else(|| val.get("default_raw"))
                .or_else(|| val.get("defaultRaw"))
            {
                return *val;
            }
            false
        }
        _ => false,
    }
}