Skip to content

Commit

Permalink
Added from_* functions for easy deserialization for crates using ni…
Browse files Browse the repository at this point in the history
…ckel as a lib (#2099)

* Added `from_*` functions for easy deserialization for crates using nickel as a lib

* Added field `files` to `EvalOrDeserError::Nickel`
Changed `from_input` to write to `NullReporter`
  • Loading branch information
rben01 authored Nov 25, 2024
1 parent f1c826d commit 77f6e75
Showing 1 changed file with 132 additions and 0 deletions.
132 changes: 132 additions & 0 deletions core/src/deserialize.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
//! 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::{
Deserialize, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess,
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};
Expand All @@ -30,6 +35,86 @@ macro_rules! deserialize_number {
};
}

#[derive(Debug)]
pub enum EvalOrDeserError {
Nickel {
error: error::Error,
files: Option<crate::files::Files>,
},
Deser(RustDeserializationError),
}

impl EvalOrDeserError {
fn new<E>(error: E, files: crate::files::Files) -> Self
where
E: Into<error::Error>,
{
Self::Nickel {
error: error.into(),
files: Some(files),
}
}
}

impl<E: Into<error::Error>> From<E> for EvalOrDeserError {
fn from(err: E) -> Self {
Self::Nickel {
error: err.into(),
files: None,
}
}
}

impl From<RustDeserializationError> for EvalOrDeserError {
fn from(err: RustDeserializationError) -> Self {
Self::Deser(err)
}
}

fn from_input<'a, T, Content, Source>(input: Input<Content, Source>) -> Result<T, EvalOrDeserError>
where
T: serde::Deserialize<'a>,
Content: std::io::Read,
Source: Into<OsString>,
{
let mut program =
Program::<CacheImpl>::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<T, EvalOrDeserError>
where
T: serde::Deserialize<'a>,
{
from_input(Input::Source(Cursor::new(s), "string"))
}

pub fn from_slice<'a, T>(v: &'a [u8]) -> Result<T, EvalOrDeserError>
where
T: serde::Deserialize<'a>,
{
from_input(Input::Source(Cursor::new(v), "slice"))
}

pub fn from_path<T>(path: impl Into<OsString>) -> Result<T, EvalOrDeserError>
where
T: serde::de::DeserializeOwned,
{
from_input(Input::<std::fs::File, _>::Path(path))
}

pub fn from_reader<R, T>(rdr: R) -> Result<T, EvalOrDeserError>
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 {
Expand Down Expand Up @@ -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::{
Expand Down Expand Up @@ -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<String>,
}

let a = A {
a: 10.0,
b: vec!["a".into(), "b".into()],
};

assert_eq!(
from_str::<A>(r#"{ a = (10 | Number), b = ["a", "b"] }"#).unwrap(),
a.clone()
);

assert_eq!(
from_slice::<A>(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::<f64>(
nickel_lang_utils::project_root::project_root()
.join("examples/fibonacci/fibonacci.ncl")
)
.unwrap(),
55.0
);

assert!(from_str::<String>("will not parse").is_err());
assert!(from_str::<String>("1 | String").is_err());
}
}

0 comments on commit 77f6e75

Please sign in to comment.