#[macro_export]
#[doc(hidden)]
macro_rules! __goish_field_convert {
($v:expr, string) => { $crate::__goish_into_string($v) };
($v:expr, [] string) => { $v };
($v:expr, [] $_t:tt) => { $v };
($v:expr, $_ty:tt) => { $v };
}
#[doc(hidden)]
pub fn __goish_into_string<T: Into<String>>(v: T) -> String { v.into() }
#[macro_export]
macro_rules! Struct {
(type $name:ident struct { $($body:tt)* }) => {
$crate::__goish_struct_parse!(@start [$name] [] [] $($body)*);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __goish_struct_parse {
(@start [$name:ident] [$($fields:tt)*] [$($order:tt)*]) => {
$crate::__goish_struct_emit!([$name] [$($fields)*] [$($order)*]);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident , $($rest:tt)*) => {
$crate::__goish_struct_parse!(@collect [$name] [$($fd)*] [$($ord)*] [$f] $($rest)*);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident $h:ident $(:: $s:ident)+ ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : ($h $(:: $s)+) ,)]
[$($ord)* ($f ($h $(:: $s)+))]
$($rest)*);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident $h:ident $(:: $s:ident)+) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : ($h $(:: $s)+) ,)]
[$($ord)* ($f ($h $(:: $s)+))]);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident [ ] $inner:tt ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : ( $crate::types::slice<$crate::__goish_type!($inner)> ) ,)]
[$($ord)* ($f ( $crate::types::slice<$crate::__goish_type!($inner)> ))]
$($rest)*);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident [ ] $inner:tt) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : ( $crate::types::slice<$crate::__goish_type!($inner)> ) ,)]
[$($ord)* ($f ( $crate::types::slice<$crate::__goish_type!($inner)> ))]);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident $ty:tt ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : $ty ,)]
[$($ord)* ($f $ty)]
$($rest)*);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident $ty:tt) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : $ty ,)]
[$($ord)* ($f $ty)]);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident $ty:ty ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : ($ty) ,)]
[$($ord)* ($f ($ty))]
$($rest)*);
};
(@start [$name:ident] [$($fd:tt)*] [$($ord:tt)*] $f:ident $ty:ty) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* ($f : ($ty) ,)]
[$($ord)* ($f ($ty))]);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $next:ident , $($rest:tt)*) => {
$crate::__goish_struct_parse!(@collect [$name] [$($fd)*] [$($ord)*] [$($names)+ $next] $($rest)*);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident $h:ident $(:: $s:ident)+ ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : ($h $(:: $s)+) ,) )+ ($last : ($h $(:: $s)+) ,)]
[$($ord)* $( ($names ($h $(:: $s)+)) )+ ($last ($h $(:: $s)+))]
$($rest)*);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident $h:ident $(:: $s:ident)+) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : ($h $(:: $s)+) ,) )+ ($last : ($h $(:: $s)+) ,)]
[$($ord)* $( ($names ($h $(:: $s)+)) )+ ($last ($h $(:: $s)+))]);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident [ ] $inner:tt ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : ( $crate::types::slice<$crate::__goish_type!($inner)> ) ,) )+
($last : ( $crate::types::slice<$crate::__goish_type!($inner)> ) ,)]
[$($ord)* $( ($names ( $crate::types::slice<$crate::__goish_type!($inner)> )) )+
($last ( $crate::types::slice<$crate::__goish_type!($inner)> ))]
$($rest)*);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident [ ] $inner:tt) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : ( $crate::types::slice<$crate::__goish_type!($inner)> ) ,) )+
($last : ( $crate::types::slice<$crate::__goish_type!($inner)> ) ,)]
[$($ord)* $( ($names ( $crate::types::slice<$crate::__goish_type!($inner)> )) )+
($last ( $crate::types::slice<$crate::__goish_type!($inner)> ))]);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident $ty:tt ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : $ty ,) )+ ($last : $ty ,)]
[$($ord)* $( ($names $ty) )+ ($last $ty)]
$($rest)*);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident $ty:tt) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : $ty ,) )+ ($last : $ty ,)]
[$($ord)* $( ($names $ty) )+ ($last $ty)]);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident $ty:ty ; $($rest:tt)*) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : ($ty) ,) )+ ($last : ($ty) ,)]
[$($ord)* $( ($names ($ty)) )+ ($last ($ty))]
$($rest)*);
};
(@collect [$name:ident] [$($fd:tt)*] [$($ord:tt)*] [$($names:ident)+] $last:ident $ty:ty) => {
$crate::__goish_struct_parse!(@start [$name]
[$($fd)* $( ($names : ($ty) ,) )+ ($last : ($ty) ,)]
[$($ord)* $( ($names ($ty)) )+ ($last ($ty))]);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __goish_struct_emit {
([$name:ident] [$( ($fn:ident : $ft:tt ,) )*] [$( ($on:ident $ot:tt) )*]) => {
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[allow(non_snake_case)]
pub struct $name {
$( pub $fn: $crate::__goish_type!($ft), )*
}
$crate::__goish_struct_ctor!($name; $( ($on $ot) )*);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __goish_type {
(string) => { $crate::types::string };
(int) => { $crate::types::int };
(int64) => { $crate::types::int64 };
(int32) => { $crate::types::int32 };
(byte) => { $crate::types::byte };
(rune) => { $crate::types::rune };
(bool) => { bool };
(float64) => { $crate::types::float64 };
(float32) => { $crate::types::float32 };
([ ] string) => { $crate::types::slice<$crate::types::string> };
([ ] int) => { $crate::types::slice<$crate::types::int> };
([ ] int64) => { $crate::types::slice<$crate::types::int64> };
([ ] byte) => { $crate::types::slice<$crate::types::byte> };
([ ] bool) => { $crate::types::slice<bool> };
([ ] float64) => { $crate::types::slice<$crate::types::float64> };
($t:ty) => { $t };
}
#[macro_export]
#[doc(hidden)]
macro_rules! __goish_struct_ctor {
($name:ident; ($on1:ident $ot1:tt)) => {
#[macro_export]
#[allow(non_snake_case)]
macro_rules! $name {
($a1:expr) => {
$name { $on1: $crate::__goish_cast!($a1, $ot1) }
};
}
};
($name:ident; ($on1:ident $ot1:tt) ($on2:ident $ot2:tt)) => {
#[macro_export]
#[allow(non_snake_case)]
macro_rules! $name {
($a1:expr, $a2:expr) => {
$name {
$on1: $crate::__goish_cast!($a1, $ot1),
$on2: $crate::__goish_cast!($a2, $ot2),
}
};
}
};
($name:ident; ($on1:ident $ot1:tt) ($on2:ident $ot2:tt) ($on3:ident $ot3:tt)) => {
#[macro_export]
#[allow(non_snake_case)]
macro_rules! $name {
($a1:expr, $a2:expr, $a3:expr) => {
$name {
$on1: $crate::__goish_cast!($a1, $ot1),
$on2: $crate::__goish_cast!($a2, $ot2),
$on3: $crate::__goish_cast!($a3, $ot3),
}
};
}
};
($name:ident; ($on1:ident $ot1:tt) ($on2:ident $ot2:tt) ($on3:ident $ot3:tt) ($on4:ident $ot4:tt)) => {
#[macro_export]
#[allow(non_snake_case)]
macro_rules! $name {
($a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$name {
$on1: $crate::__goish_cast!($a1, $ot1),
$on2: $crate::__goish_cast!($a2, $ot2),
$on3: $crate::__goish_cast!($a3, $ot3),
$on4: $crate::__goish_cast!($a4, $ot4),
}
};
}
};
($name:ident; ($on1:ident $ot1:tt) ($on2:ident $ot2:tt) ($on3:ident $ot3:tt) ($on4:ident $ot4:tt) ($on5:ident $ot5:tt)) => {
#[macro_export]
#[allow(non_snake_case)]
macro_rules! $name {
($a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$name {
$on1: $crate::__goish_cast!($a1, $ot1),
$on2: $crate::__goish_cast!($a2, $ot2),
$on3: $crate::__goish_cast!($a3, $ot3),
$on4: $crate::__goish_cast!($a4, $ot4),
$on5: $crate::__goish_cast!($a5, $ot5),
}
};
}
};
($name:ident; ($on1:ident $ot1:tt) ($on2:ident $ot2:tt) ($on3:ident $ot3:tt) ($on4:ident $ot4:tt) ($on5:ident $ot5:tt) ($on6:ident $ot6:tt)) => {
#[macro_export]
#[allow(non_snake_case)]
macro_rules! $name {
($a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$name {
$on1: $crate::__goish_cast!($a1, $ot1),
$on2: $crate::__goish_cast!($a2, $ot2),
$on3: $crate::__goish_cast!($a3, $ot3),
$on4: $crate::__goish_cast!($a4, $ot4),
$on5: $crate::__goish_cast!($a5, $ot5),
$on6: $crate::__goish_cast!($a6, $ot6),
}
};
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __goish_cast {
($v:expr, string) => { ($v).into() };
($v:expr, $_ty:tt) => { $v };
}
#[cfg(test)]
mod tests {
Struct!{ type PathTest struct { path, result string } }
#[test]
fn path_test_positional_construction() {
let t = PathTest!("abc", "def");
assert_eq!(t.path, "abc");
assert_eq!(t.result, "def");
}
Struct!{ type IsAbsTest struct { path string; isAbs bool } }
#[test]
fn is_abs_test_positional_construction() {
let t = IsAbsTest!("/foo", true);
assert_eq!(t.path, "/foo");
assert_eq!(t.isAbs, true);
}
Struct!{ type Triple struct { a, b, c string } }
#[test]
fn triple_construction() {
let t = Triple!("x", "y", "z");
assert_eq!(t.a, "x");
assert_eq!(t.b, "y");
assert_eq!(t.c, "z");
}
Struct!{ type Mixed struct { name string; count int; ok bool } }
#[test]
fn mixed_types() {
let t = Mixed!("alpha", 42i64, true);
assert_eq!(t.name, "alpha");
assert_eq!(t.count, 42);
assert_eq!(t.ok, true);
}
mod semver {
#[allow(non_snake_case)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Version { pub Major: i64, pub Minor: i64, pub Patch: i64 }
}
Struct!{ type tcase struct {
name string;
ver1 semver::Version;
ver2 semver::Version;
expected int
} }
Struct!{ type Row struct { Method, Pattern, Args string } }
#[test]
fn struct_usable_as_map_key_and_in_hashset() {
use std::collections::HashSet;
let a = Row!("GET", "/users", "id=1");
let b = Row!("GET", "/users", "id=1");
assert_eq!(a, b);
let mut s: HashSet<Row> = HashSet::new();
s.insert(a);
assert!(s.contains(&b));
}
Struct!{ type Bag struct { name string; items []string } }
#[test]
fn slice_field_types() {
let b = Bag!(
"basket",
vec![crate::gostring::GoString::from("a"), "b".into()].into()
);
assert_eq!(b.name, "basket");
assert_eq!(b.items.len(), 2);
assert_eq!(b.items[0i64], "a");
}
Struct!{ type Parts struct { head, tail []int } }
#[test]
fn multi_name_slice_fields() {
let empty: crate::types::slice<i64> = crate::types::slice::new();
let p = Parts!(empty.clone(), empty);
assert_eq!(p.head.len(), 0);
assert_eq!(p.tail.len(), 0);
}
Struct!{ type OptHolder struct {
name string;
maybe Option<bool>;
items Vec<i64>
} }
#[test]
fn generic_field_types() {
let h = OptHolder!("test", Some(true), vec![1, 2, 3]);
assert_eq!(h.name, "test");
assert_eq!(h.maybe, Some(true));
assert_eq!(h.items, vec![1, 2, 3]);
}
#[test]
fn qualified_path_field_types() {
let t = tcase!(
"1.2.0 vs 1.3.0",
semver::Version { Major: 1, Minor: 2, Patch: 0 },
semver::Version { Major: 1, Minor: 3, Patch: 0 },
-1i64
);
assert_eq!(t.name, "1.2.0 vs 1.3.0");
assert_eq!(t.ver1.Minor, 2);
assert_eq!(t.ver2.Minor, 3);
assert_eq!(t.expected, -1);
}
}