diff --git a/.travis.yml b/.travis.yml index b6e442587..943864703 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ script: - | travis-cargo build && travis-cargo test && - travis-cargo test -- -p frunk_core && + cd core && travis-cargo test && travis-cargo doc after_success: diff --git a/Cargo.toml b/Cargo.toml index 9237a6118..db3885fcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "frunk" -version = "0.1.19" +version = "0.1.20" authors = ["Lloyd "] description = "Frunk provides developers with a number of functional programming tools like HList, Generic, LabelledGeneric, Validated, Monoid, Semigroup and friends." license = "MIT" documentation = "https://docs.rs/frunk" repository = "https://github.com/lloydmeta/frunk" -keywords = ["Frunk", "HList", "Generic", "Validated", "Semigroup", "Monoid"] +keywords = ["Frunk", "HList", "Generic", "Validated", "Monoid"] [badges] travis-ci = { repository = "lloydmeta/frunk" } @@ -16,8 +16,8 @@ time = "0.1.36" [dependencies.frunk_core] path = "core" -version = "0.0.9" +version = "0.0.10" [dependencies.frunk_derives] path = "derives" -version = "0.0.10" \ No newline at end of file +version = "0.0.11" \ No newline at end of file diff --git a/core/Cargo.toml b/core/Cargo.toml index 46a9ef6f1..ca8da57b4 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frunk_core" -version = "0.0.9" +version = "0.0.10" authors = ["Lloyd "] description = "Frunk core provides developers with HList and Generic" license = "MIT" @@ -10,3 +10,7 @@ keywords = ["Frunk", "HList", "Generic", "LabelledGeneric"] [badges] travis-ci = { repository = "lloydmeta/frunk" } + +[dev-dependencies.frunk_derives] +path = "../derives" +version = "0.0.11" diff --git a/core/src/generic.rs b/core/src/generic.rs index ec97c1133..53ba5e73a 100644 --- a/core/src/generic.rs +++ b/core/src/generic.rs @@ -8,14 +8,15 @@ /// For the most part, you should be using the derivation that is available through /// frunk_derive to generate instances of this typeclass for your types. /// -/// I would highly recommend you check out `derivation_tests.rs` to see how to actually use -/// this trait in real life. Since frunk_derive depends on this trait, I can't actually -/// pull it in as a dependency here (otherwise the dependency would be circular) and show -/// how to use it in a proper doc test. -/// /// # Examples /// -/// ```rust,ignore +/// ```rust +/// #[allow(unused_imports)] +/// # #[macro_use] extern crate frunk_derives; +/// # #[macro_use] extern crate frunk_core; +/// # use frunk_core::hlist::*; fn main() { +/// use frunk_core::hlist::*; +/// use frunk_core::generic::*; /// #[derive(Generic)] /// struct ApiPerson<'a> { /// FirstName: &'a str, @@ -31,11 +32,12 @@ /// } /// /// let a_person = ApiPerson { -/// first_name: "Joe", -/// last_name: "Blow", -/// age: 30, +/// FirstName: "Joe", +/// LastName: "Blow", +/// Age: 30, /// }; /// let d_person: DomainPerson = convert_from(a_person); // done +/// # } /// ``` pub trait Generic { /// The generic representation type diff --git a/core/src/hlist.rs b/core/src/hlist.rs index 319a430d8..7549fd5ff 100644 --- a/core/src/hlist.rs +++ b/core/src/hlist.rs @@ -175,7 +175,13 @@ pub fn h_cons(h: H, tail: T) -> HCons { /// let (h1, (h2, h3)) = h.into_tuple2(); /// assert_eq!(h1, 13.5f32); /// assert_eq!(h2, "hello"); -/// assert_eq!(h3, Some(41)) +/// assert_eq!(h3, Some(41)); +/// +/// // Also works when you have trailing commas +/// let h4 = hlist!["yo",]; +/// let h5 = hlist![13.5f32, "hello", Some(41),]; +/// assert_eq!(h4, hlist!["yo"]); +/// assert_eq!(h5, hlist![13.5f32, "hello", Some(41)]); /// # } /// ``` #[macro_export] @@ -193,6 +199,16 @@ macro_rules! hlist { $crate::hlist::HCons { head: $first, tail: hlist!($($repeated), *)} }; + // <-- Forwarding of trailing comma variants + ($first: expr, $( $repeated: expr, ) +) => { + hlist!($first, $($repeated),*) + }; + + ($first: expr, ) => { + hlist!($first) + }; + // Forwarding of trailing comma variants --> + } /// Macro for pattern-matching on HLists. @@ -207,7 +223,7 @@ macro_rules! hlist { /// let hlist_pat![h1, h2, h3] = h; /// assert_eq!(h1, 13.5f32); /// assert_eq!(h2, "hello"); -/// assert_eq!(h3, Some(41)) +/// assert_eq!(h3, Some(41)); /// # } /// ``` #[macro_export] @@ -215,6 +231,11 @@ macro_rules! hlist_pat { {} => { $crate::hlist::HNil }; { $head:pat, $($tail:tt), +} => { $crate::hlist::HCons{ head: $head, tail: hlist_pat!($($tail),*) } }; { $head:pat } => { $crate::hlist::HCons { head: $head, tail: $crate::hlist::HNil } }; + + // <-- Forward trailing comma variants + { $head:pat, $($tail:tt,) +} => { hlist_pat!($head, $($tail),*) }; + { $head:pat, } => { hlist_pat!($head) }; + // Forward trailing comma variants --> } /// Returns a type signature for an HList of the provided types @@ -242,6 +263,16 @@ macro_rules! Hlist { ($first: ty, $( $repeated: ty ), +) => { $crate::hlist::HCons<$first, Hlist!($($repeated), *)> }; + + // <-- Forward trailing comma variants + ($single: ty,) => { + Hlist![$single] + }; + + ($first: ty, $( $repeated: ty, ) +) => { + Hlist![$first, $($repeated),*] + }; + // Forward trailing comma variants --> } impl Add for HNil @@ -407,12 +438,11 @@ impl Sculptor for Source { /// Indices is HCons here because the compiler is being asked to figure out the /// Index for Plucking the first item of type THead out of Self and the rest (IndexTail) is for the /// Plucker's remainder induce. -impl Sculptor, HCons> - for HCons +impl Sculptor, HCons> +for HCons where HCons: Plucker, as Plucker>::Remainder: Sculptor { - type Remainder = < as Plucker>::Remainder as Sculptor>::Remainder; fn sculpt(self) -> (HCons, Self::Remainder) { @@ -426,7 +456,6 @@ impl Sculptor IntoReverse for HCons fn into_reverse(self) -> Self::Output { self.tail.into_reverse() + - HCons { - head: self.head, - tail: HNil, - } + HCons { + head: self.head, + tail: HNil, + } } } @@ -624,7 +653,7 @@ impl HFoldLeftable for HNil { } impl HFoldLeftable, Acc> - for HCons +for HCons where Tail: HFoldLeftable, F: FnOnce(Acc, H) -> FolderHeadR { @@ -714,16 +743,14 @@ mod tests { #[test] fn test_pluck() { - let h = hlist![1, "hello", true, 42f32]; let (t, r): (f32, _) = h.pluck(); assert_eq!(t, 42f32); assert_eq!(r, hlist![1, "hello", true]) - } #[test] - fn test_macro() { + fn test_hlist_macro() { assert_eq!(hlist![], HNil); let h: Hlist !(i32, &str, i32) = hlist![1, "2", 3]; @@ -738,14 +765,37 @@ mod tests { assert_eq!(tail3, HNil); } + + #[test] + #[allow(non_snake_case)] + fn test_Hlist_macro() { + let h1: Hlist!(i32, &str, i32) = hlist![1, "2", 3]; + let h2: Hlist!(i32, &str, i32,) = hlist![1, "2", 3]; + let h3: Hlist!(i32) = hlist![1]; + let h4: Hlist!(i32,) = hlist![1,]; + assert_eq!(h1, h2); + assert_eq!(h3, h4); + } + #[test] fn test_pattern_matching() { + let hlist_pat!(one1) = hlist!["one"]; + assert_eq!(one1, "one"); + let hlist_pat!(one2,) = hlist!["one"]; + assert_eq!(one2, "one"); + let h = hlist![5, 3.2f32, true, "blue".to_owned()]; let hlist_pat!(five, float, right, s) = h; assert_eq!(five, 5); assert_eq!(float, 3.2f32); assert_eq!(right, true); assert_eq!(s, "blue".to_owned()); + + let h2 = hlist![13.5f32, "hello", Some(41)]; + let hlist_pat![a, b, c,] = h2; + assert_eq!(a, 13.5f32); + assert_eq!(b, "hello"); + assert_eq!(c, Some(41)); } #[test] @@ -793,11 +843,9 @@ mod tests { #[test] fn test_sculpt() { - let h = hlist![9000, "joe", 41f32]; let (reshaped, remainder): (Hlist!(f32, i32), _) = h.sculpt(); assert_eq!(reshaped, hlist![41f32, 9000]); assert_eq!(remainder, hlist!["joe"]) - } } diff --git a/core/src/labelled.rs b/core/src/labelled.rs index 7b521b306..fbffe7c1d 100644 --- a/core/src/labelled.rs +++ b/core/src/labelled.rs @@ -30,17 +30,21 @@ use std::fmt; /// A trait that converts from a type to a labelled generic representation /// +/// LabelledGenerics allow us to have completely type-safe, boilerplate free conversions +/// between different structs. +/// /// For the most part, you should be using the derivation that is available through /// frunk_derive to generate instances of this typeclass for your types. /// -/// I would highly recommend you check out `derivation_tests.rs` to see how to actually use -/// this trait in real life. Since frunk_derive depends on this trait, I can't actually -/// pull it in as a dependency here (otherwise the dependency would be circular) and show -/// how to use it in a proper doc test. -/// /// # Examples /// -/// ```rust,ignore +/// ```rust +/// #[allow(unused_imports)] +/// # #[macro_use] extern crate frunk_derives; +/// # #[macro_use] extern crate frunk_core; +/// # use frunk_core::hlist::*; fn main() { +/// use frunk_core::hlist::*; +/// use frunk_core::labelled::*; /// #[derive(LabelledGeneric)] /// struct NewUser<'a> { /// first_name: &'a str, @@ -48,11 +52,12 @@ use std::fmt; /// age: usize, /// } /// +/// // Notice that the fields are mismatched in terms of ordering /// #[derive(LabelledGeneric)] /// struct SavedUser<'a> { -/// first_name: &'a str, /// last_name: &'a str, /// age: usize, +/// first_name: &'a str, /// } /// /// let n_user = NewUser { @@ -61,8 +66,10 @@ use std::fmt; /// age: 30, /// }; /// -/// let s_user = ::sculpted_convert_from(n_user); // done -/// ``` +/// // sculpted_convert_from automagically sculpts the labelled generic +/// // representation of the source object to that of the target type +/// let s_user: SavedUser = sculpted_convert_from(n_user); // done +/// # } pub trait LabelledGeneric { /// The labelled generic representation type type Repr; diff --git a/derives/Cargo.toml b/derives/Cargo.toml index 0cc261b0c..3c83d012b 100644 --- a/derives/Cargo.toml +++ b/derives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frunk_derives" -version = "0.0.10" +version = "0.0.11" authors = ["Lloyd "] description = "frunk_derives contains the custom derivations for certain traits in Frunk." license = "MIT" @@ -20,4 +20,4 @@ quote = "0.3.15" [dependencies.frunk_core] path = "../core" -version = "0.0.9" +version = "0.0.10" diff --git a/derives/src/lib.rs b/derives/src/lib.rs index fd06488d5..4252aaa19 100644 --- a/derives/src/lib.rs +++ b/derives/src/lib.rs @@ -8,7 +8,6 @@ //! 2. [Crates.io page](https://crates.io/crates/frunk) extern crate proc_macro; -#[macro_use] extern crate frunk_core; #[macro_use] extern crate quote; diff --git a/src/lib.rs b/src/lib.rs index 75b2bdb8e..4e8ea97a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,8 +101,10 @@ //! 1. [Source on Github](https://github.com/lloydmeta/frunk) //! 2. [Crates.io page](https://crates.io/crates/frunk) +#[allow(unused_imports)] #[macro_use] extern crate frunk_core; +#[allow(unused_imports)] #[macro_use] extern crate frunk_derives; diff --git a/tests/validated_tests.rs b/tests/validated_tests.rs index 515ec71cf..2f23f7bd1 100644 --- a/tests/validated_tests.rs +++ b/tests/validated_tests.rs @@ -1,5 +1,4 @@ extern crate frunk; -#[macro_use] // for the hlist macro extern crate frunk_core; use frunk::*; // for the Generic trait and HList