xs/nu/commands/
cas_command.rs

1use std::io::Read;
2
3use nu_engine::CallExt;
4use nu_protocol::engine::{Call, Command, EngineState, Stack};
5use nu_protocol::{Category, PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
6
7use crate::store::Store;
8
9#[derive(Clone)]
10pub struct CasCommand {
11    store: Store,
12}
13
14impl CasCommand {
15    pub fn new(store: Store) -> Self {
16        Self { store }
17    }
18}
19
20impl Command for CasCommand {
21    fn name(&self) -> &str {
22        ".cas"
23    }
24
25    fn signature(&self) -> Signature {
26        Signature::build(".cas")
27            .input_output_types(vec![(Type::Nothing, Type::String)])
28            .required(
29                "hash",
30                SyntaxShape::String,
31                "hash of the content to retrieve",
32            )
33            .category(Category::Experimental)
34    }
35
36    fn description(&self) -> &str {
37        "Retrieve content from the CAS for the given hash"
38    }
39
40    fn run(
41        &self,
42        engine_state: &EngineState,
43        stack: &mut Stack,
44        call: &Call,
45        _input: PipelineData,
46    ) -> Result<PipelineData, ShellError> {
47        let span = call.head;
48        let hash: String = call.req(engine_state, stack, 0)?;
49        let hash: ssri::Integrity = hash.parse().map_err(|e| ShellError::GenericError {
50            error: "I/O Error".into(),
51            msg: format!("Malformed ssri::Integrity:: {e}"),
52            span: Some(span),
53            help: None,
54            inner: vec![],
55        })?;
56
57        let mut reader =
58            self.store
59                .cas_reader_sync(hash)
60                .map_err(|e| ShellError::GenericError {
61                    error: "I/O Error".into(),
62                    msg: e.to_string(),
63                    span: Some(span),
64                    help: None,
65                    inner: vec![],
66                })?;
67
68        let mut contents = Vec::new();
69        reader
70            .read_to_end(&mut contents)
71            .map_err(|e| ShellError::GenericError {
72                error: "I/O Error".into(),
73                msg: e.to_string(),
74                span: Some(span),
75                help: None,
76                inner: vec![],
77            })?;
78
79        // Try to convert to string if valid UTF-8, otherwise return as binary
80        let value = match String::from_utf8(contents.clone()) {
81            Ok(string) => Value::string(string, span),
82            Err(_) => Value::binary(contents, span),
83        };
84
85        Ok(PipelineData::Value(value, None))
86    }
87}