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, Option)], 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 { 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, 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, Option)>; fn deserialize_named(span: Span, reader: evaluated_call::Reader) -> Result { 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, Option)> = 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::(); 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 { let message_reader = serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap(); let reader = message_reader .get_root::() .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 = 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"), } }); } }