diff --git a/core/src/deserialize.rs b/core/src/deserialize.rs index 75f74d5ad..a702df051 100644 --- a/core/src/deserialize.rs +++ b/core/src/deserialize.rs @@ -1,6 +1,8 @@ //! Deserialization of an evaluated program to plain Rust types. use malachite::{num::conversion::traits::RoundingFrom, rounding_modes::RoundingMode}; +use std::ffi::OsString; +use std::io::Cursor; use std::iter::ExactSizeIterator; use serde::de::{ @@ -8,7 +10,10 @@ use serde::de::{ VariantAccess, Visitor, }; +use crate::error::{self, NullReporter}; +use crate::eval::cache::CacheImpl; use crate::identifier::LocIdent; +use crate::program::{Input, Program}; use crate::term::array::Array; use crate::term::record::Field; use crate::term::{IndexMap, RichTerm, Term}; @@ -30,6 +35,86 @@ macro_rules! deserialize_number { }; } +#[derive(Debug)] +pub enum EvalOrDeserError { + Nickel { + error: error::Error, + files: Option, + }, + Deser(RustDeserializationError), +} + +impl EvalOrDeserError { + fn new(error: E, files: crate::files::Files) -> Self + where + E: Into, + { + Self::Nickel { + error: error.into(), + files: Some(files), + } + } +} + +impl> From for EvalOrDeserError { + fn from(err: E) -> Self { + Self::Nickel { + error: err.into(), + files: None, + } + } +} + +impl From for EvalOrDeserError { + fn from(err: RustDeserializationError) -> Self { + Self::Deser(err) + } +} + +fn from_input<'a, T, Content, Source>(input: Input) -> Result +where + T: serde::Deserialize<'a>, + Content: std::io::Read, + Source: Into, +{ + let mut program = + Program::::new_from_input(input, std::io::stderr(), NullReporter {}) + .map_err(error::IOError::from)?; + + Ok(T::deserialize(program.eval_full_for_export().map_err( + |err| EvalOrDeserError::new(err, program.files()), + )?)?) +} + +pub fn from_str<'a, T>(s: &'a str) -> Result +where + T: serde::Deserialize<'a>, +{ + from_input(Input::Source(Cursor::new(s), "string")) +} + +pub fn from_slice<'a, T>(v: &'a [u8]) -> Result +where + T: serde::Deserialize<'a>, +{ + from_input(Input::Source(Cursor::new(v), "slice")) +} + +pub fn from_path(path: impl Into) -> Result +where + T: serde::de::DeserializeOwned, +{ + from_input(Input::::Path(path)) +} + +pub fn from_reader(rdr: R) -> Result +where + R: std::io::Read, + T: serde::de::DeserializeOwned, +{ + from_input(Input::Source(rdr, "reader")) +} + /// An error occurred during deserialization to Rust. #[derive(Debug, PartialEq, Eq, Clone)] pub enum RustDeserializationError { @@ -586,6 +671,7 @@ impl serde::de::Error for RustDeserializationError { #[cfg(test)] mod tests { + use super::{from_path, from_reader, from_slice, from_str}; use std::io::Cursor; use nickel_lang_utils::{ @@ -691,4 +777,50 @@ mod tests { A { a: 10.0 } ) } + + #[test] + fn rust_deserialize_wrappers() { + #[derive(Debug, Clone, PartialEq, Deserialize)] + struct A { + a: f64, + b: Vec, + } + + let a = A { + a: 10.0, + b: vec!["a".into(), "b".into()], + }; + + assert_eq!( + from_str::(r#"{ a = (10 | Number), b = ["a", "b"] }"#).unwrap(), + a.clone() + ); + + assert_eq!( + from_slice::(br#"{ a = (10 | Number), b = ["a", "b"] }"#).unwrap(), + a.clone() + ); + + assert_eq!( + from_reader::<_, A>(Cursor::new(r#"{ a = (10 | Number), b = ["a", "b"] }"#)).unwrap(), + a.clone() + ); + + assert_eq!( + from_reader::<_, A>(Cursor::new(br#"{ a = (10 | Number), b = ["a", "b"] }"#)).unwrap(), + a.clone() + ); + + assert_eq!( + from_path::( + nickel_lang_utils::project_root::project_root() + .join("examples/fibonacci/fibonacci.ncl") + ) + .unwrap(), + 55.0 + ); + + assert!(from_str::("will not parse").is_err()); + assert!(from_str::("1 | String").is_err()); + } }