summaryrefslogtreecommitdiff
path: root/serde_dhall
diff options
context:
space:
mode:
authorNadrieril2020-03-23 22:30:48 +0000
committerNadrieril2020-03-31 21:45:32 +0100
commit8e74abfc55f86dbd9142a9f38e070d2583454ddd (patch)
tree01a3119f5fc3e02744aa26ac28ae09f90fc37f3d /serde_dhall
parent25c879e802e90a447e10e5f9a0f522217e34f20d (diff)
Go mad with doc macros
Diffstat (limited to 'serde_dhall')
-rw-r--r--serde_dhall/Cargo.toml2
-rw-r--r--serde_dhall/src/lib.rs5
-rw-r--r--serde_dhall/src/shortcuts.rs366
3 files changed, 267 insertions, 106 deletions
diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml
index 7fd7b3e..6af883a 100644
--- a/serde_dhall/Cargo.toml
+++ b/serde_dhall/Cargo.toml
@@ -13,9 +13,9 @@ edition = "2018"
serde = { version = "1.0", features = ["derive"] }
dhall = { version = "=0.4.0", path = "../dhall" }
dhall_proc_macros = { version = "=0.4.0", path = "../dhall_proc_macros" }
+doc-comment = "0.3"
reqwest = { version = "0.10", features = ["blocking"] }
url = "2.1"
[dev-dependencies]
-doc-comment = "0.3"
version-sync = "0.8"
diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs
index 02b34a7..4cb25e5 100644
--- a/serde_dhall/src/lib.rs
+++ b/serde_dhall/src/lib.rs
@@ -189,6 +189,9 @@ pub use dhall_proc_macros::StaticType;
pub use deserialize::Deserialize;
pub(crate) use deserialize::Sealed;
pub use error::{Error, Result};
-pub use shortcuts::{from_str, from_str_manual_type, from_str_static_type};
+pub use shortcuts::{
+ from_file, from_file_manual_type, from_file_static_type, from_str,
+ from_str_manual_type, from_str_static_type,
+};
pub use static_type::StaticType;
pub use value::{SimpleType, Value};
diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs
index 54888c5..4aba9d1 100644
--- a/serde_dhall/src/shortcuts.rs
+++ b/serde_dhall/src/shortcuts.rs
@@ -1,111 +1,269 @@
+use doc_comment::doc_comment;
+use std::path::Path;
+
use crate::{options, Deserialize, Result, SimpleType, StaticType};
-/// 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 current directory.
-/// See [`options`] for more control over this process.
-///
-/// For additional type safety, prefer [`from_str_static_type`] or [`from_str_manual_type`].
-///
-/// [`options`]: options/index.html
-/// [`from_str_manual_type`]: fn.from_str_manual_type.html
-/// [`from_str_static_type`]: fn.from_str_static_type.html
-///
-/// # Example
-///
-/// ```rust
-/// # fn main() -> serde_dhall::Result<()> {
-/// use serde::Deserialize;
-///
-/// // We use serde's derive feature
-/// #[derive(Debug, Deserialize)]
-/// struct Point {
-/// x: u64,
-/// y: u64,
-/// }
-///
-/// // Some Dhall data
-/// let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
-///
-/// // Parse the Dhall string as a Point.
-/// let point: Point = serde_dhall::from_str(data)?;
-///
-/// assert_eq!(point.x, 1);
-/// assert_eq!(point.y, 2);
-///
-/// # Ok(())
-/// # }
-/// ```
-pub fn from_str<T>(s: &str) -> Result<T>
-where
- T: Deserialize,
-{
- options::from_str(s).parse()
+// Avoid copy-pasting documentation
+
+#[rustfmt::skip]
+macro_rules! gen_doc {
+ (@source_desc, str) => {"a string of Dhall text"};
+ (@source_desc, file) => {"a Dhall file"};
+ (@source_desc, url) => {"a remote url"};
+
+ (@tck_info1, none) => {""};
+ (@tck_info1, manual) => {", additionally checking that it matches the supplied type"};
+ (@tck_info1, static) => {", additionally checking that it matches the type of `T`"};
+
+ (@tck_info2, none) => {""};
+ (@tck_info2, manual) => {" against the supplied type"};
+ (@tck_info2, static) => {" against the type of `T`"};
+
+ (@tck_req, none) => {""};
+ (@tck_req, manual) => {""};
+ (@tck_req, static) => {"`T` must implement the [`StaticType`] trait.\n"};
+
+ (@tck_comment, $src:tt, none) => {
+ concat!("For additional type safety, prefer [`from_", stringify!($src), "_static_type`]
+ or [`from_", stringify!($src), "_manual_type`].\n")
+ };
+ (@tck_comment, $src:tt, manual) => {concat!("See also [`from_", stringify!($src), "_static_type`].\n")};
+ (@tck_comment, $src:tt, static) => {""};
+
+ (@run_example, str) => {""};
+ (@run_example, file) => {"no_run"};
+ (@run_example, url) => {"no_run"};
+
+ ($src:tt, $ty:tt) => {concat!("
+Deserialize an instance of type `T` from ", gen_doc!(@source_desc, $src), gen_doc!(@tck_info1, $ty),".
+
+", gen_doc!(@tck_req, $ty), "
+This will recursively resolve all imports in the expression, and typecheck it", gen_doc!(@tck_info2, $ty),"
+before deserialization. Relative imports will be resolved relative to the current directory.
+See [`options`] for more control over this process.
+
+", gen_doc!(@tck_comment, $src, $ty), "
+
+# Example
+
+```", gen_doc!(@run_example, $src), "
+# fn main() -> serde_dhall::Result<()> {",
+gen_example!($src, $ty), "
+# Ok(())
+# }
+```
+
+[`options`]: options/index.html
+[`from_", stringify!($src), "_manual_type`]: fn.from_", stringify!($src), "_manual_type.html
+[`from_", stringify!($src), "_static_type`]: fn.from_", stringify!($src), "_static_type.html
+[`StaticType`]: trait.StaticType.html
+")};
+}
+
+#[rustfmt::skip]
+macro_rules! gen_example {
+ (str, none) => {concat!(r#"
+use serde::Deserialize;
+
+// We use serde's derive feature
+#[derive(Debug, Deserialize)]
+struct Point {
+ x: u64,
+ y: u64,
+}
+
+// Some Dhall data
+let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }";
+
+// Parse the Dhall string as a Point.
+let point: Point = serde_dhall::from_str(data)?;
+
+assert_eq!(point.x, 1);
+assert_eq!(point.y, 2);
+"#)};
+
+ (str, manual) => {concat!(r#"
+use std::collections::HashMap;
+use serde_dhall::SimpleType;
+
+// Parse a Dhall type
+let point_type_str = "{ x: Natural, y: Natural }";
+let point_type: SimpleType = serde_dhall::from_str(point_type_str)?;
+
+// Some Dhall data
+let point_data = "{ x = 1, y = 1 + 1 }";
+
+// Deserialize the data to a Rust type. This checks that
+// the data matches the provided type.
+let deserialized_map: HashMap<String, usize> =
+ serde_dhall::from_str_manual_type(point_data, &point_type)?;
+
+let mut expected_map = HashMap::new();
+expected_map.insert("x".to_string(), 1);
+expected_map.insert("y".to_string(), 2);
+
+assert_eq!(deserialized_map, expected_map);
+"#)};
+
+ (str, static) => {concat!(r#"
+use serde::Deserialize;
+use serde_dhall::StaticType;
+
+#[derive(Debug, Deserialize, StaticType)]
+struct Point {
+ x: u64,
+ y: u64,
}
-/// Deserialize an instance of type `T` from a string of Dhall text,
-/// additionally checking that it matches the supplied type.
-///
-/// This will recursively resolve all imports in the expression, and typecheck it against the
-/// supplied type before deserialization. Relative imports will be resolved relative to the current
-/// directory. See [`options`] for more control over this process.
-///
-/// See also [`from_str_static_type`].
-///
-/// [`options`]: options/index.html
-/// [`from_str_static_type`]: fn.from_str_static_type.html
-///
-/// # Example
-///
-/// ```rust
-/// # fn main() -> serde_dhall::Result<()> {
-/// use serde_dhall::SimpleType;
-/// use std::collections::HashMap;
-///
-/// // Parse a Dhall type
-/// let point_type_str = "{ x: Natural, y: Natural }";
-/// let point_type: SimpleType = serde_dhall::from_str(point_type_str)?;
-///
-/// // Some Dhall data
-/// let point_data = "{ x = 1, y = 1 + 1 }";
-///
-/// // Deserialize the data to a Rust type. This checks that
-/// // the data matches the provided type.
-/// let deserialized_map: HashMap<String, usize> =
-/// serde_dhall::from_str_manual_type(point_data, &point_type)?;
-///
-/// let mut expected_map = HashMap::new();
-/// expected_map.insert("x".to_string(), 1);
-/// expected_map.insert("y".to_string(), 2);
-///
-/// assert_eq!(deserialized_map, expected_map);
-/// # Ok(())
-/// # }
-/// ```
-pub fn from_str_manual_type<T>(s: &str, ty: &SimpleType) -> Result<T>
-where
- T: Deserialize,
-{
- options::from_str(s).type_annotation(ty).parse()
+// Some Dhall data
+let data = "{ x = 1, y = 1 + 1 }";
+
+// Convert the Dhall string to a Point.
+let point: Point = serde_dhall::from_str_static_type(data)?;
+assert_eq!(point.x, 1);
+assert_eq!(point.y, 2);
+
+// Invalid data fails the type validation
+let invalid_data = "{ x = 1, z = 0.3 }";
+assert!(serde_dhall::from_str_static_type::<Point>(invalid_data).is_err());
+"#)};
+
+ (file, none) => {concat!(r#"
+use serde::Deserialize;
+
+// We use serde's derive feature
+#[derive(Debug, Deserialize)]
+struct Point {
+ x: u64,
+ y: u64,
}
-/// Deserialize an instance of type `T` from a string of Dhall text,
-/// additionally checking that it matches the type of `T`.
-///
-/// `T` must implement the [`StaticType`] trait.
-///
-/// This will recursively resolve all imports in the expression, and typecheck it against the
-/// type of `T`. Relative imports will be resolved relative to the current
-/// directory. See [`options`] for more control over this process.
-///
-/// [`options`]: options/index.html
-/// [`StaticType`]: trait.StaticType.html
-///
-/// TODO
-pub fn from_str_static_type<T>(s: &str) -> Result<T>
-where
- T: Deserialize + StaticType,
-{
- options::from_str(s).static_type_annotation().parse()
+// Parse the Dhall file as a Point.
+let point: Point = serde_dhall::from_file("foo.dhall")?;
+"#)};
+
+ (file, manual) => {concat!(r#"
+use std::collections::HashMap;
+use serde_dhall::SimpleType;
+
+// Parse a Dhall type
+let point_type_str = "{ x: Natural, y: Natural }";
+let point_type: SimpleType = serde_dhall::from_str(point_type_str)?;
+
+// Deserialize the data to a Rust type. This checks that
+// the data matches the provided type.
+let deserialized_map: HashMap<String, usize> =
+ serde_dhall::from_file_manual_type("foo.dhall", &point_type)?;
+"#)};
+
+ (file, static) => {concat!(r#"
+use serde::Deserialize;
+use serde_dhall::StaticType;
+
+#[derive(Debug, Deserialize, StaticType)]
+struct Point {
+ x: u64,
+ y: u64,
+}
+
+// Convert the Dhall string to a Point.
+let point: Point = serde_dhall::from_file_static_type("foo.dhall")?;
+"#)};
+
+ ($src:tt, $ty:tt) => {""};
}
+
+macro_rules! generate_fn {
+ (@generate_src,
+ str, $ty:tt, $name:ident,
+ ) => {
+ generate_fn!(@generate_ty,
+ str, $ty, $name,
+ (),
+ (s: &str),
+ (options::from_str(s)),
+ );
+ };
+ (@generate_src,
+ file, $ty:tt, $name:ident,
+ ) => {
+ generate_fn!(@generate_ty,
+ file, $ty, $name,
+ (P: AsRef<Path>),
+ (path: P),
+ (options::from_file(path)),
+ );
+ };
+
+ (@generate_ty,
+ $src:tt, none, $name:ident,
+ ($($ty_params:tt)*),
+ ($($input_args:tt)*),
+ ($($create_options:tt)*),
+ ) => {
+ generate_fn!(@generate,
+ $src, none, $name,
+ ($($ty_params)*),
+ ($($input_args)*),
+ (),
+ ($($create_options)*),
+ );
+ };
+ (@generate_ty,
+ $src:tt, manual, $name:ident,
+ ($($ty_params:tt)*),
+ ($($input_args:tt)*),
+ ($($create_options:tt)*),
+ ) => {
+ generate_fn!(@generate,
+ $src, manual, $name,
+ ($($ty_params)*),
+ ($($input_args)*, ty: &SimpleType),
+ (),
+ ($($create_options)* .type_annotation(ty)),
+ );
+ };
+ (@generate_ty,
+ $src:tt, static, $name:ident,
+ ($($ty_params:tt)*),
+ ($($input_args:tt)*),
+ ($($create_options:tt)*),
+ ) => {
+ generate_fn!(@generate,
+ $src, static, $name,
+ ($($ty_params)*),
+ ($($input_args)*),
+ (+ StaticType),
+ ($($create_options)* .static_type_annotation()),
+ );
+ };
+
+ (@generate,
+ $src:tt, $ty:tt, $name:ident,
+ ($($ty_params:tt)*),
+ ($($input_args:tt)*),
+ ($($extra_bounds:tt)*),
+ ($($create_options:tt)*),
+ ) => {
+ doc_comment! {
+ gen_doc!($src, $ty),
+ pub fn $name<T, $($ty_params)*> ($($input_args)*) -> Result<T>
+ where
+ T: Deserialize $($extra_bounds)*,
+ {
+ $($create_options)* .parse()
+ }
+ }
+ };
+
+ ($src:tt, $ty:tt, $name:ident) => {
+ generate_fn!(@generate_src, $src, $ty, $name,);
+ };
+}
+
+generate_fn!(str, none, from_str);
+generate_fn!(str, manual, from_str_manual_type);
+generate_fn!(str, static, from_str_static_type);
+generate_fn!(file, none, from_file);
+generate_fn!(file, manual, from_file_manual_type);
+generate_fn!(file, static, from_file_static_type);