Skip to content

Commit

Permalink
Trailing commas in Hlist macros, more doc tests (lloydmeta#44)
Browse files Browse the repository at this point in the history
* Add support for trailing commas in hlist! and Hlist!

* Support trailing comma pattern matches as well

* Stop ignoring doctests for LabelledGeneric and Generic

* Try tweaking travis.yml

* Core 0.0.10 release

* Derives 0.0.11 release

* 0.1.20 release

* One less kw
  • Loading branch information
lloydmeta authored Mar 17, 2017
1 parent d2d6bbb commit 28f1dc8
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[package]
name = "frunk"
version = "0.1.19"
version = "0.1.20"
authors = ["Lloyd <[email protected]>"]
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" }
Expand All @@ -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"
version = "0.0.11"
6 changes: 5 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "frunk_core"
version = "0.0.9"
version = "0.0.10"
authors = ["Lloyd <[email protected]>"]
description = "Frunk core provides developers with HList and Generic"
license = "MIT"
Expand All @@ -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"
20 changes: 11 additions & 9 deletions core/src/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
80 changes: 64 additions & 16 deletions core/src/hlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,13 @@ pub fn h_cons<H, T: HList>(h: H, tail: T) -> HCons<H, T> {
/// 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]
Expand All @@ -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.
Expand All @@ -207,14 +223,19 @@ 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]
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
Expand Down Expand Up @@ -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<RHS> Add<RHS> for HNil
Expand Down Expand Up @@ -407,12 +438,11 @@ impl<Source> Sculptor<HNil, HNil> for Source {
/// Indices is HCons<IndexHead, IndexTail> 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 <THead, TTail, SHead, STail, IndexHead, IndexTail> Sculptor<HCons<THead, TTail>, HCons<IndexHead, IndexTail>>
for HCons<SHead, STail>
impl<THead, TTail, SHead, STail, IndexHead, IndexTail> Sculptor<HCons<THead, TTail>, HCons<IndexHead, IndexTail>>
for HCons<SHead, STail>
where
HCons<SHead, STail>: Plucker<THead, IndexHead>,
<HCons<SHead, STail> as Plucker<THead, IndexHead>>::Remainder: Sculptor<TTail, IndexTail> {

type Remainder = <<HCons<SHead, STail> as Plucker<THead, IndexHead>>::Remainder as Sculptor<TTail, IndexTail>>::Remainder;

fn sculpt(self) -> (HCons<THead, TTail>, Self::Remainder) {
Expand All @@ -426,7 +456,6 @@ impl <THead, TTail, SHead, STail, IndexHead, IndexTail> Sculptor<HCons<THead, TT
tail_remainder
)
}

}


Expand Down Expand Up @@ -468,10 +497,10 @@ impl<H, Tail> IntoReverse for HCons<H, Tail>

fn into_reverse(self) -> Self::Output {
self.tail.into_reverse() +
HCons {
head: self.head,
tail: HNil,
}
HCons {
head: self.head,
tail: HNil,
}
}
}

Expand Down Expand Up @@ -624,7 +653,7 @@ impl<F, Acc> HFoldLeftable<F, Acc> for HNil {
}

impl<F, FolderHeadR, FolderTail, H, Tail, Acc> HFoldLeftable<HCons<F, FolderTail>, Acc>
for HCons<H, Tail>
for HCons<H, Tail>
where Tail: HFoldLeftable<FolderTail, FolderHeadR>,
F: FnOnce(Acc, H) -> FolderHeadR
{
Expand Down Expand Up @@ -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];
Expand All @@ -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]
Expand Down Expand Up @@ -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"])

}
}
25 changes: 16 additions & 9 deletions core/src/labelled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,34 @@ 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,
/// last_name: &'a str,
/// 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 {
Expand All @@ -61,8 +66,10 @@ use std::fmt;
/// age: 30,
/// };
///
/// let s_user = <SavedUser as LabelledGeneric>::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;
Expand Down
4 changes: 2 additions & 2 deletions derives/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "frunk_derives"
version = "0.0.10"
version = "0.0.11"
authors = ["Lloyd <[email protected]>"]
description = "frunk_derives contains the custom derivations for certain traits in Frunk."
license = "MIT"
Expand All @@ -20,4 +20,4 @@ quote = "0.3.15"

[dependencies.frunk_core]
path = "../core"
version = "0.0.9"
version = "0.0.10"
1 change: 0 additions & 1 deletion derives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 0 additions & 1 deletion tests/validated_tests.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 28f1dc8

Please sign in to comment.