Skip to content

Commit 28f1dc8

Browse files
authored
Trailing commas in Hlist macros, more doc tests (#44)
* 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
1 parent d2d6bbb commit 28f1dc8

10 files changed

Lines changed: 105 additions & 44 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ script:
1515
- |
1616
travis-cargo build &&
1717
travis-cargo test &&
18-
travis-cargo test -- -p frunk_core &&
18+
cd core && travis-cargo test &&
1919
travis-cargo doc
2020
2121
after_success:

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[package]
22
name = "frunk"
3-
version = "0.1.19"
3+
version = "0.1.20"
44
authors = ["Lloyd <lloydmeta@gmail.com>"]
55
description = "Frunk provides developers with a number of functional programming tools like HList, Generic, LabelledGeneric, Validated, Monoid, Semigroup and friends."
66
license = "MIT"
77
documentation = "https://docs.rs/frunk"
88
repository = "https://github.com/lloydmeta/frunk"
9-
keywords = ["Frunk", "HList", "Generic", "Validated", "Semigroup", "Monoid"]
9+
keywords = ["Frunk", "HList", "Generic", "Validated", "Monoid"]
1010

1111
[badges]
1212
travis-ci = { repository = "lloydmeta/frunk" }
@@ -16,8 +16,8 @@ time = "0.1.36"
1616

1717
[dependencies.frunk_core]
1818
path = "core"
19-
version = "0.0.9"
19+
version = "0.0.10"
2020

2121
[dependencies.frunk_derives]
2222
path = "derives"
23-
version = "0.0.10"
23+
version = "0.0.11"

core/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "frunk_core"
3-
version = "0.0.9"
3+
version = "0.0.10"
44
authors = ["Lloyd <lloydmeta@gmail.com>"]
55
description = "Frunk core provides developers with HList and Generic"
66
license = "MIT"
@@ -10,3 +10,7 @@ keywords = ["Frunk", "HList", "Generic", "LabelledGeneric"]
1010

1111
[badges]
1212
travis-ci = { repository = "lloydmeta/frunk" }
13+
14+
[dev-dependencies.frunk_derives]
15+
path = "../derives"
16+
version = "0.0.11"

core/src/generic.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
/// For the most part, you should be using the derivation that is available through
99
/// frunk_derive to generate instances of this typeclass for your types.
1010
///
11-
/// I would highly recommend you check out `derivation_tests.rs` to see how to actually use
12-
/// this trait in real life. Since frunk_derive depends on this trait, I can't actually
13-
/// pull it in as a dependency here (otherwise the dependency would be circular) and show
14-
/// how to use it in a proper doc test.
15-
///
1611
/// # Examples
1712
///
18-
/// ```rust,ignore
13+
/// ```rust
14+
/// #[allow(unused_imports)]
15+
/// # #[macro_use] extern crate frunk_derives;
16+
/// # #[macro_use] extern crate frunk_core;
17+
/// # use frunk_core::hlist::*; fn main() {
18+
/// use frunk_core::hlist::*;
19+
/// use frunk_core::generic::*;
1920
/// #[derive(Generic)]
2021
/// struct ApiPerson<'a> {
2122
/// FirstName: &'a str,
@@ -31,11 +32,12 @@
3132
/// }
3233
///
3334
/// let a_person = ApiPerson {
34-
/// first_name: "Joe",
35-
/// last_name: "Blow",
36-
/// age: 30,
35+
/// FirstName: "Joe",
36+
/// LastName: "Blow",
37+
/// Age: 30,
3738
/// };
3839
/// let d_person: DomainPerson = convert_from(a_person); // done
40+
/// # }
3941
/// ```
4042
pub trait Generic {
4143
/// The generic representation type

core/src/hlist.rs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,13 @@ pub fn h_cons<H, T: HList>(h: H, tail: T) -> HCons<H, T> {
175175
/// let (h1, (h2, h3)) = h.into_tuple2();
176176
/// assert_eq!(h1, 13.5f32);
177177
/// assert_eq!(h2, "hello");
178-
/// assert_eq!(h3, Some(41))
178+
/// assert_eq!(h3, Some(41));
179+
///
180+
/// // Also works when you have trailing commas
181+
/// let h4 = hlist!["yo",];
182+
/// let h5 = hlist![13.5f32, "hello", Some(41),];
183+
/// assert_eq!(h4, hlist!["yo"]);
184+
/// assert_eq!(h5, hlist![13.5f32, "hello", Some(41)]);
179185
/// # }
180186
/// ```
181187
#[macro_export]
@@ -193,6 +199,16 @@ macro_rules! hlist {
193199
$crate::hlist::HCons { head: $first, tail: hlist!($($repeated), *)}
194200
};
195201

202+
// <-- Forwarding of trailing comma variants
203+
($first: expr, $( $repeated: expr, ) +) => {
204+
hlist!($first, $($repeated),*)
205+
};
206+
207+
($first: expr, ) => {
208+
hlist!($first)
209+
};
210+
// Forwarding of trailing comma variants -->
211+
196212
}
197213

198214
/// Macro for pattern-matching on HLists.
@@ -207,14 +223,19 @@ macro_rules! hlist {
207223
/// let hlist_pat![h1, h2, h3] = h;
208224
/// assert_eq!(h1, 13.5f32);
209225
/// assert_eq!(h2, "hello");
210-
/// assert_eq!(h3, Some(41))
226+
/// assert_eq!(h3, Some(41));
211227
/// # }
212228
/// ```
213229
#[macro_export]
214230
macro_rules! hlist_pat {
215231
{} => { $crate::hlist::HNil };
216232
{ $head:pat, $($tail:tt), +} => { $crate::hlist::HCons{ head: $head, tail: hlist_pat!($($tail),*) } };
217233
{ $head:pat } => { $crate::hlist::HCons { head: $head, tail: $crate::hlist::HNil } };
234+
235+
// <-- Forward trailing comma variants
236+
{ $head:pat, $($tail:tt,) +} => { hlist_pat!($head, $($tail),*) };
237+
{ $head:pat, } => { hlist_pat!($head) };
238+
// Forward trailing comma variants -->
218239
}
219240

220241
/// Returns a type signature for an HList of the provided types
@@ -242,6 +263,16 @@ macro_rules! Hlist {
242263
($first: ty, $( $repeated: ty ), +) => {
243264
$crate::hlist::HCons<$first, Hlist!($($repeated), *)>
244265
};
266+
267+
// <-- Forward trailing comma variants
268+
($single: ty,) => {
269+
Hlist![$single]
270+
};
271+
272+
($first: ty, $( $repeated: ty, ) +) => {
273+
Hlist![$first, $($repeated),*]
274+
};
275+
// Forward trailing comma variants -->
245276
}
246277

247278
impl<RHS> Add<RHS> for HNil
@@ -407,12 +438,11 @@ impl<Source> Sculptor<HNil, HNil> for Source {
407438
/// Indices is HCons<IndexHead, IndexTail> here because the compiler is being asked to figure out the
408439
/// Index for Plucking the first item of type THead out of Self and the rest (IndexTail) is for the
409440
/// Plucker's remainder induce.
410-
impl <THead, TTail, SHead, STail, IndexHead, IndexTail> Sculptor<HCons<THead, TTail>, HCons<IndexHead, IndexTail>>
411-
for HCons<SHead, STail>
441+
impl<THead, TTail, SHead, STail, IndexHead, IndexTail> Sculptor<HCons<THead, TTail>, HCons<IndexHead, IndexTail>>
442+
for HCons<SHead, STail>
412443
where
413444
HCons<SHead, STail>: Plucker<THead, IndexHead>,
414445
<HCons<SHead, STail> as Plucker<THead, IndexHead>>::Remainder: Sculptor<TTail, IndexTail> {
415-
416446
type Remainder = <<HCons<SHead, STail> as Plucker<THead, IndexHead>>::Remainder as Sculptor<TTail, IndexTail>>::Remainder;
417447

418448
fn sculpt(self) -> (HCons<THead, TTail>, Self::Remainder) {
@@ -426,7 +456,6 @@ impl <THead, TTail, SHead, STail, IndexHead, IndexTail> Sculptor<HCons<THead, TT
426456
tail_remainder
427457
)
428458
}
429-
430459
}
431460

432461

@@ -468,10 +497,10 @@ impl<H, Tail> IntoReverse for HCons<H, Tail>
468497

469498
fn into_reverse(self) -> Self::Output {
470499
self.tail.into_reverse() +
471-
HCons {
472-
head: self.head,
473-
tail: HNil,
474-
}
500+
HCons {
501+
head: self.head,
502+
tail: HNil,
503+
}
475504
}
476505
}
477506

@@ -624,7 +653,7 @@ impl<F, Acc> HFoldLeftable<F, Acc> for HNil {
624653
}
625654

626655
impl<F, FolderHeadR, FolderTail, H, Tail, Acc> HFoldLeftable<HCons<F, FolderTail>, Acc>
627-
for HCons<H, Tail>
656+
for HCons<H, Tail>
628657
where Tail: HFoldLeftable<FolderTail, FolderHeadR>,
629658
F: FnOnce(Acc, H) -> FolderHeadR
630659
{
@@ -714,16 +743,14 @@ mod tests {
714743

715744
#[test]
716745
fn test_pluck() {
717-
718746
let h = hlist![1, "hello", true, 42f32];
719747
let (t, r): (f32, _) = h.pluck();
720748
assert_eq!(t, 42f32);
721749
assert_eq!(r, hlist![1, "hello", true])
722-
723750
}
724751

725752
#[test]
726-
fn test_macro() {
753+
fn test_hlist_macro() {
727754
assert_eq!(hlist![], HNil);
728755
let h: Hlist
729756
!(i32, &str, i32) = hlist![1, "2", 3];
@@ -738,14 +765,37 @@ mod tests {
738765
assert_eq!(tail3, HNil);
739766
}
740767

768+
769+
#[test]
770+
#[allow(non_snake_case)]
771+
fn test_Hlist_macro() {
772+
let h1: Hlist!(i32, &str, i32) = hlist![1, "2", 3];
773+
let h2: Hlist!(i32, &str, i32,) = hlist![1, "2", 3];
774+
let h3: Hlist!(i32) = hlist![1];
775+
let h4: Hlist!(i32,) = hlist![1,];
776+
assert_eq!(h1, h2);
777+
assert_eq!(h3, h4);
778+
}
779+
741780
#[test]
742781
fn test_pattern_matching() {
782+
let hlist_pat!(one1) = hlist!["one"];
783+
assert_eq!(one1, "one");
784+
let hlist_pat!(one2,) = hlist!["one"];
785+
assert_eq!(one2, "one");
786+
743787
let h = hlist![5, 3.2f32, true, "blue".to_owned()];
744788
let hlist_pat!(five, float, right, s) = h;
745789
assert_eq!(five, 5);
746790
assert_eq!(float, 3.2f32);
747791
assert_eq!(right, true);
748792
assert_eq!(s, "blue".to_owned());
793+
794+
let h2 = hlist![13.5f32, "hello", Some(41)];
795+
let hlist_pat![a, b, c,] = h2;
796+
assert_eq!(a, 13.5f32);
797+
assert_eq!(b, "hello");
798+
assert_eq!(c, Some(41));
749799
}
750800

751801
#[test]
@@ -793,11 +843,9 @@ mod tests {
793843

794844
#[test]
795845
fn test_sculpt() {
796-
797846
let h = hlist![9000, "joe", 41f32];
798847
let (reshaped, remainder): (Hlist!(f32, i32), _) = h.sculpt();
799848
assert_eq!(reshaped, hlist![41f32, 9000]);
800849
assert_eq!(remainder, hlist!["joe"])
801-
802850
}
803851
}

core/src/labelled.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,34 @@ use std::fmt;
3030

3131
/// A trait that converts from a type to a labelled generic representation
3232
///
33+
/// LabelledGenerics allow us to have completely type-safe, boilerplate free conversions
34+
/// between different structs.
35+
///
3336
/// For the most part, you should be using the derivation that is available through
3437
/// frunk_derive to generate instances of this typeclass for your types.
3538
///
36-
/// I would highly recommend you check out `derivation_tests.rs` to see how to actually use
37-
/// this trait in real life. Since frunk_derive depends on this trait, I can't actually
38-
/// pull it in as a dependency here (otherwise the dependency would be circular) and show
39-
/// how to use it in a proper doc test.
40-
///
4139
/// # Examples
4240
///
43-
/// ```rust,ignore
41+
/// ```rust
42+
/// #[allow(unused_imports)]
43+
/// # #[macro_use] extern crate frunk_derives;
44+
/// # #[macro_use] extern crate frunk_core;
45+
/// # use frunk_core::hlist::*; fn main() {
46+
/// use frunk_core::hlist::*;
47+
/// use frunk_core::labelled::*;
4448
/// #[derive(LabelledGeneric)]
4549
/// struct NewUser<'a> {
4650
/// first_name: &'a str,
4751
/// last_name: &'a str,
4852
/// age: usize,
4953
/// }
5054
///
55+
/// // Notice that the fields are mismatched in terms of ordering
5156
/// #[derive(LabelledGeneric)]
5257
/// struct SavedUser<'a> {
53-
/// first_name: &'a str,
5458
/// last_name: &'a str,
5559
/// age: usize,
60+
/// first_name: &'a str,
5661
/// }
5762
///
5863
/// let n_user = NewUser {
@@ -61,8 +66,10 @@ use std::fmt;
6166
/// age: 30,
6267
/// };
6368
///
64-
/// let s_user = <SavedUser as LabelledGeneric>::sculpted_convert_from(n_user); // done
65-
/// ```
69+
/// // sculpted_convert_from automagically sculpts the labelled generic
70+
/// // representation of the source object to that of the target type
71+
/// let s_user: SavedUser = sculpted_convert_from(n_user); // done
72+
/// # }
6673
pub trait LabelledGeneric {
6774
/// The labelled generic representation type
6875
type Repr;

derives/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "frunk_derives"
3-
version = "0.0.10"
3+
version = "0.0.11"
44
authors = ["Lloyd <lloydmeta@gmail.com>"]
55
description = "frunk_derives contains the custom derivations for certain traits in Frunk."
66
license = "MIT"
@@ -20,4 +20,4 @@ quote = "0.3.15"
2020

2121
[dependencies.frunk_core]
2222
path = "../core"
23-
version = "0.0.9"
23+
version = "0.0.10"

derives/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
//! 2. [Crates.io page](https://crates.io/crates/frunk)
99
1010
extern crate proc_macro;
11-
#[macro_use]
1211
extern crate frunk_core;
1312
#[macro_use]
1413
extern crate quote;

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,10 @@
101101
//! 1. [Source on Github](https://github.com/lloydmeta/frunk)
102102
//! 2. [Crates.io page](https://crates.io/crates/frunk)
103103
104+
#[allow(unused_imports)]
104105
#[macro_use]
105106
extern crate frunk_core;
107+
#[allow(unused_imports)]
106108
#[macro_use]
107109
extern crate frunk_derives;
108110

tests/validated_tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
extern crate frunk;
2-
#[macro_use] // for the hlist macro
32
extern crate frunk_core;
43

54
use frunk::*; // for the Generic trait and HList

0 commit comments

Comments
 (0)