223 lines
6.6 KiB
Rust
223 lines
6.6 KiB
Rust
use super::value;
|
|
use crate::{evaluated_call::EvaluatedCall, plugin_capnp::evaluated_call};
|
|
use nu_protocol::{ShellError, Span, Spanned, Value};
|
|
|
|
pub(crate) fn serialize_call(
|
|
call: &EvaluatedCall,
|
|
mut builder: evaluated_call::Builder,
|
|
) -> Result<(), ShellError> {
|
|
let mut head = builder.reborrow().init_head();
|
|
head.set_start(call.head.start as u64);
|
|
head.set_end(call.head.end as u64);
|
|
|
|
serialize_positional(&call.positional, builder.reborrow());
|
|
serialize_named(&call.named, builder)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn serialize_positional(positional: &[Value], mut builder: evaluated_call::Builder) {
|
|
let mut positional_builder = builder.reborrow().init_positional(positional.len() as u32);
|
|
|
|
for (index, value) in positional.iter().enumerate() {
|
|
value::serialize_value(value, positional_builder.reborrow().get(index as u32))
|
|
}
|
|
}
|
|
|
|
fn serialize_named(
|
|
named: &[(Spanned<String>, Option<Value>)],
|
|
mut builder: evaluated_call::Builder,
|
|
) -> Result<(), ShellError> {
|
|
let mut named_builder = builder
|
|
.reborrow()
|
|
.init_named()
|
|
.init_entries(named.len() as u32);
|
|
|
|
for (index, (key, expression)) in named.iter().enumerate() {
|
|
let mut entry_builder = named_builder.reborrow().get(index as u32);
|
|
entry_builder
|
|
.reborrow()
|
|
.set_key(key.item.as_str())
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
if let Some(value) = expression {
|
|
let value_builder = entry_builder.init_value();
|
|
value::serialize_value(value, value_builder);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn deserialize_call(
|
|
reader: evaluated_call::Reader,
|
|
) -> Result<EvaluatedCall, ShellError> {
|
|
let head_reader = reader
|
|
.get_head()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
let head = Span {
|
|
start: head_reader.get_start() as usize,
|
|
end: head_reader.get_end() as usize,
|
|
};
|
|
|
|
let positional = deserialize_positionals(head, reader)?;
|
|
let named = deserialize_named(head, reader)?;
|
|
|
|
Ok(EvaluatedCall {
|
|
head,
|
|
positional,
|
|
named,
|
|
})
|
|
}
|
|
|
|
fn deserialize_positionals(
|
|
_span: Span,
|
|
reader: evaluated_call::Reader,
|
|
) -> Result<Vec<Value>, ShellError> {
|
|
let positional_reader = reader
|
|
.get_positional()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
positional_reader
|
|
.iter()
|
|
.map(value::deserialize_value)
|
|
.collect()
|
|
}
|
|
|
|
type NamedList = Vec<(Spanned<String>, Option<Value>)>;
|
|
|
|
fn deserialize_named(span: Span, reader: evaluated_call::Reader) -> Result<NamedList, ShellError> {
|
|
let named_reader = reader
|
|
.get_named()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
let entries_list = named_reader
|
|
.get_entries()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
let mut entries: Vec<(Spanned<String>, Option<Value>)> =
|
|
Vec::with_capacity(entries_list.len() as usize);
|
|
|
|
for entry_reader in entries_list {
|
|
let item = entry_reader
|
|
.get_key()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?
|
|
.to_string();
|
|
|
|
let value = if entry_reader.has_value() {
|
|
let value_reader = entry_reader
|
|
.get_value()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
let value = value::deserialize_value(value_reader)
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
Some(value)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let key = Spanned { item, span };
|
|
|
|
entries.push((key, value))
|
|
}
|
|
|
|
Ok(entries)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use capnp::serialize;
|
|
use core::panic;
|
|
|
|
use super::*;
|
|
use nu_protocol::{Span, Spanned, Value};
|
|
|
|
fn write_buffer(
|
|
call: &EvaluatedCall,
|
|
writer: &mut impl std::io::Write,
|
|
) -> Result<(), ShellError> {
|
|
let mut message = ::capnp::message::Builder::new_default();
|
|
|
|
let builder = message.init_root::<evaluated_call::Builder>();
|
|
serialize_call(call, builder)?;
|
|
|
|
serialize::write_message(writer, &message)
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))
|
|
}
|
|
|
|
fn read_buffer(reader: &mut impl std::io::BufRead) -> Result<EvaluatedCall, ShellError> {
|
|
let message_reader =
|
|
serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap();
|
|
|
|
let reader = message_reader
|
|
.get_root::<evaluated_call::Reader>()
|
|
.map_err(|e| ShellError::PluginFailedToLoad(e.to_string()))?;
|
|
|
|
deserialize_call(reader)
|
|
}
|
|
|
|
#[test]
|
|
fn call_round_trip() {
|
|
let call = EvaluatedCall {
|
|
head: Span { start: 0, end: 10 },
|
|
positional: vec![
|
|
Value::Float {
|
|
val: 1.0,
|
|
span: Span { start: 0, end: 10 },
|
|
},
|
|
Value::String {
|
|
val: "something".into(),
|
|
span: Span { start: 0, end: 10 },
|
|
},
|
|
],
|
|
named: vec![
|
|
(
|
|
Spanned {
|
|
item: "name".to_string(),
|
|
span: Span { start: 0, end: 10 },
|
|
},
|
|
Some(Value::Float {
|
|
val: 1.0,
|
|
span: Span { start: 0, end: 10 },
|
|
}),
|
|
),
|
|
(
|
|
Spanned {
|
|
item: "flag".to_string(),
|
|
span: Span { start: 0, end: 10 },
|
|
},
|
|
None,
|
|
),
|
|
],
|
|
};
|
|
|
|
let mut buffer: Vec<u8> = Vec::new();
|
|
write_buffer(&call, &mut buffer).expect("unable to serialize message");
|
|
let returned_call = read_buffer(&mut buffer.as_slice()).expect("unable to read buffer");
|
|
|
|
assert_eq!(call.head, returned_call.head);
|
|
assert_eq!(call.positional.len(), returned_call.positional.len());
|
|
|
|
call.positional
|
|
.iter()
|
|
.zip(returned_call.positional.iter())
|
|
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
|
|
|
call.named
|
|
.iter()
|
|
.zip(returned_call.named.iter())
|
|
.for_each(|(lhs, rhs)| {
|
|
// Comparing the keys
|
|
assert_eq!(lhs.0.item, rhs.0.item);
|
|
|
|
match (&lhs.1, &rhs.1) {
|
|
(None, None) => {}
|
|
(Some(a), Some(b)) => assert_eq!(a, b),
|
|
_ => panic!("not matching values"),
|
|
}
|
|
});
|
|
}
|
|
}
|