summaryrefslogtreecommitdiff
path: root/dhall/src/lib.rs
blob: c85b962b96260e3c0cb1df38ec81ea4ec06e01b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#![feature(trace_macros)]
#![feature(proc_macro_hygiene)]
#![feature(slice_patterns)]
#![feature(label_break_value)]
#![feature(non_exhaustive)]
#![feature(bind_by_move_pattern_guards)]
#![cfg_attr(test, feature(custom_inner_attributes))]
#![allow(
    clippy::type_complexity,
    clippy::infallible_destructuring_match,
    clippy::many_single_char_names,
    clippy::match_wild_err_arm
)]

//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive
//! alternative to JSON and YAML.
//!
//! You can think of Dhall as: JSON + types + imports + functions
//!
//! For a description of the dhall language, examples, tutorials, and more, see the [language
//! website][dhall].
//!
//! This crate provides support for consuming dhall files the same way you would consume JSON or
//! YAML. It uses the [Serde][serde] serialization library to provide drop-in support for dhall
//! for any datatype that supports serde (and that's a lot of them !).
//!
//! This library is limited to deserializing (reading) dhall values; serializing (writing)
//! values to dhall is not supported for now.
//!
//! # Examples
//!
//! ### Custom datatype
//!
//! If you have a custom datatype for which you derived [serde::Deserialize], chances are
//! you will be able to derive [SimpleStaticType][de::SimpleStaticType] for it as well.
//! This gives you access to a dhall representation of your datatype that can be outputted
//! to users, and allows easy type-safe deserializing.
//!
//! ```edition2018
//! use serde::Deserialize;
//! use dhall::de::SimpleStaticType;
//!
//! #[derive(Debug, Deserialize, SimpleStaticType)]
//! struct Point {
//!     x: u64,
//!     y: u64,
//! }
//!
//! fn main() {
//!     // Some dhall data
//!     let data = "{ x = 1, y = 1 + 1 }";
//!
//!     // Convert the dhall string to a Point.
//!     let point: Point =
//!             dhall::de::from_str_auto_type(&data)
//!                 .expect("An error ocurred !");
//!
//!     // Prints "point = Point { x: 1, y: 2 }"
//!     println!("point = {:?}", point);
//! }
//! ```
//!
//! ### Loosely typed
//!
//! If you used to consume JSON or YAML in a loosely typed way, you can continue to do so
//! with dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str]
//! with [dhall::de::from_str][de::from_str].
//! More generally, if the [SimpleStaticType][de::SimpleStaticType] derive doesn't suit your
//! needs, you can still deserialize any valid dhall file that serde can handle.
//!
//! [serde_json::from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html
//! [serde_yaml::from_str]: https://docs.serde.rs/serde_yaml/fn.from_str.html
//!
//! ```edition2018
//! use std::collections::BTreeMap;
//!
//! let mut map = BTreeMap::new();
//! map.insert("x".to_string(), 1);
//! map.insert("y".to_string(), 2);
//!
//! // Some dhall data
//! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
//!
//! // Deserialize it to a Rust type.
//! let deserialized_map: BTreeMap<String, usize> =
//!         dhall::de::from_str(&data, None)
//!             .expect("Failed reading the data !");
//! assert_eq!(map, deserialized_map);
//! ```
//!
//! You can of course specify a dhall type that the input should match.
//!
//! ```edition2018
//! use std::collections::BTreeMap;
//!
//! let mut map = BTreeMap::new();
//! map.insert("x".to_string(), 1);
//! map.insert("y".to_string(), 2);
//!
//! // Some dhall data
//! let point_data = "{ x = 1, y = 1 + 1 }";
//! let point_type_data = "{ x: Natural, y: Natural }";
//!
//! // Construct a type
//! let point_type =
//!         dhall::de::from_str(point_type_data, None)
//!             .expect("Could not parse the Point type");
//!
//! // Deserialize it to a Rust type.
//! let deserialized_map: BTreeMap<String, usize> =
//!         dhall::de::from_str(&point_data, Some(&point_type))
//!             .expect("Failed reading the data !");
//! assert_eq!(map, deserialized_map);
//! ```
//!
//! [dhall]: https://dhall-lang.org/
//! [serde]: https://docs.serde.rs/serde/
//! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html

#[cfg(test)]
#[macro_use]
mod tests;

#[cfg(test)]
mod parser;

mod binary;
/// When manipulating Dhall expressions goes wrong.
pub mod error;
pub mod expr;
mod imports;
mod normalize;
mod serde;
mod traits;
mod typecheck;

/// Deserialization of Dhall expressions into Rust
pub mod de {
    pub use crate::traits::{Deserialize, SimpleStaticType, StaticType};
    #[doc(hidden)]
    pub use dhall_generator::SimpleStaticType;

    /// Deserialize an instance of type T from a string of Dhall text.
    ///
    /// This will recursively resolve all imports in the expression, and
    /// typecheck it before deserialization. Relative imports will be resolved relative to the
    /// provided file. More control over this process is not yet available
    /// but will be in a coming version of this crate.
    ///
    /// If a type is provided, this additionally checks that the provided
    /// expression has that type.
    pub fn from_str<'a, T: Deserialize<'a>>(
        s: &'a str,
        ty: Option<&crate::expr::Type>,
    ) -> crate::error::Result<T> {
        T::from_str(s, ty)
    }

    /// Deserialize an instance of type T from a string of Dhall text,
    /// additionally checking that it matches the type of T.
    ///
    /// This will recursively resolve all imports in the expression, and
    /// typecheck it before deserialization. Relative imports will be resolved relative to the
    /// provided file. More control over this process is not yet available
    /// but will be in a coming version of this crate.
    pub fn from_str_auto_type<'a, T: Deserialize<'a> + StaticType>(
        s: &'a str,
    ) -> crate::error::Result<T> {
        from_str(s, Some(&<T as StaticType>::get_static_type()))
    }
}