use std::fmt::Debug;
use crate::traits::ToDocInfo;
pub mod fields;
pub mod params;
#[derive(Clone)]
pub enum ParamAttr {
None,
Default,
Value(syn::Expr),
}
#[derive(Clone)]
pub enum PermutedItem<T: Clone> {
Positional(T),
Named(T),
Default(T),
}
impl<T: Debug + Clone> Debug for PermutedItem<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Positional(arg0) => f.debug_tuple("Positional").field(arg0).finish(),
Self::Named(arg0) => f.debug_tuple("Named").field(arg0).finish(),
Self::Default(arg0) => f.debug_tuple("Default").field(arg0).finish(),
}
}
}
impl<T: Clone + PartialEq> PartialEq for PermutedItem<T> {
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
}
impl<T: Clone + ToDocInfo> ToDocInfo for PermutedItem<T> {
fn to_doc_info(&self) -> crate::traits::DocInfo {
self.inner().to_doc_info()
}
}
impl<T: Clone> PermutedItem<T> {
fn inner(&self) -> &T {
match self {
Self::Positional(i) => i,
Self::Named(i) => i,
Self::Default(i) => i,
}
}
fn parition_named_defaults(
items: &[PermutedItem<T>],
) -> (Vec<PermutedItem<T>>, Vec<PermutedItem<T>>) {
let res: (Vec<_>, Vec<_>) = items.iter().cloned().partition(|def| match def {
PermutedItem::Named(_) => true,
PermutedItem::Default(_) => false,
_ => panic!("unexpected variant"),
});
res
}
}
#[allow(clippy::type_complexity)]
pub fn permute<T: Clone + Debug>(
required: Vec<T>,
default: Vec<T>,
) -> Vec<(Vec<PermutedItem<T>>, Vec<PermutedItem<T>>)> {
let named_permute = (0..=required.len())
.flat_map(|idx| {
let (positional, named) = required.split_at(idx);
let positional = positional
.iter()
.map(|p| PermutedItem::Positional(p.to_owned()))
.collect::<Vec<_>>();
let permute_slice = permute_named(named);
permute_slice
.iter()
.map(|named_seq| [positional.as_slice(), named_seq.as_slice()].concat())
.collect::<Vec<_>>()
.into_iter()
})
.collect::<Vec<_>>();
debug_assert!(if let Some(l) = named_permute.last() {
l.iter()
.all(|item| matches!(item, PermutedItem::Positional(_)))
} else {
true
});
let default_permute = permute_named_default(&default);
let default_positional_permute = permute_pos_default(&default);
let all_positional = match named_permute.last() {
Some(base) => {
assert!(
base.iter()
.all(|item| matches!(item, PermutedItem::Positional(_))),
"all permuted items in the last permuted sequence must be positional."
);
default_positional_permute
.into_iter()
.map(|seq| (base.to_vec(), seq.to_vec()))
.collect::<Vec<_>>()
}
None => default_positional_permute
.into_iter()
.map(|seq| (vec![], seq))
.collect(),
};
let named_pos = match (named_permute.len(), default_permute.len()) {
(0, 0) => return all_positional,
(0, _) => default_permute
.into_iter()
.map(|seq| (vec![], seq))
.collect::<Vec<_>>(),
(_, 0) => named_permute
.into_iter()
.map(|seq| (seq, vec![]))
.collect::<Vec<_>>(),
_ => named_permute
.into_iter()
.flat_map(|seq| {
default_permute
.iter()
.map(move |def_seq| (seq.to_vec(), def_seq.to_vec()))
})
.collect::<Vec<_>>(),
};
[named_pos, all_positional].concat()
}
pub fn permute_tuple_struct<T: Clone>(
required: Vec<T>,
default: Vec<T>,
) -> Vec<Vec<PermutedItem<T>>> {
let positionals = required
.into_iter()
.map(|f| PermutedItem::Positional(f))
.collect::<Vec<_>>();
let res = (0..default.len() + 1)
.map(|default_idx| {
let (def_pos, def_unused) = default.split_at(default_idx);
let def_pos_perm = def_pos
.iter()
.cloned()
.map(|f| PermutedItem::Positional(f))
.collect::<Vec<_>>();
let def_unused_perm = def_unused
.iter()
.cloned()
.map(|f| PermutedItem::Default(f))
.collect::<Vec<_>>();
[positionals.clone(), def_pos_perm, def_unused_perm].concat()
})
.collect::<Vec<_>>();
res
}
fn permute_named<T: Clone>(named: &[T]) -> Vec<Vec<PermutedItem<T>>> {
let permutations = permute::permutations_of(named);
permutations
.into_iter()
.map(|single_perm| {
single_perm
.into_iter()
.map(|item| PermutedItem::Named(item.clone()))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
}
fn permute_named_default<T: Clone + Debug>(defaults: &[T]) -> Vec<Vec<PermutedItem<T>>> {
let base_permute = (0..(1 << defaults.len()))
.rev()
.map(|num| {
let seq = defaults
.iter()
.enumerate()
.map(|(pos, item)| {
if (num >> pos) & 1 != 0 {
PermutedItem::Named(item.to_owned())
} else {
PermutedItem::Default(item.to_owned())
}
})
.collect::<Vec<_>>();
seq
})
.collect::<Vec<_>>();
let res = base_permute
.into_iter()
.flat_map(|seq| {
let (used, unused) = PermutedItem::<T>::parition_named_defaults(&seq);
let mut used_permute = permute::permute(used);
if !unused.is_empty() {
for item in &mut used_permute {
item.extend_from_slice(&unused);
}
}
used_permute.into_iter()
})
.collect::<Vec<_>>();
res.into_iter().filter(|item| !item.is_empty()).collect()
}
fn permute_pos_default<T: Clone + Debug>(defaults: &[T]) -> Vec<Vec<PermutedItem<T>>> {
let res = (1..=defaults.len())
.flat_map(|idx| {
let (positional, named) = defaults.split_at(idx);
let pos_params = positional
.iter()
.map(|p| PermutedItem::Positional(p.to_owned()))
.collect::<Vec<_>>();
let inter = match named.len() {
0 => vec![pos_params],
_ => {
let named_permute = permute_named_default(named);
named_permute
.into_iter()
.map(move |named_seq| [pos_params.clone(), named_seq].concat())
.collect::<Vec<_>>()
}
};
inter.into_iter()
})
.collect::<Vec<_>>();
res
}
#[cfg(test)]
mod tests {
use super::*;
fn idx_to_str(mut idx: usize) -> String {
let mut id = vec![];
if idx == 0 {
id.push(0);
}
while idx != 0 {
id.push(idx % 26);
idx /= 26;
}
id.into_iter()
.map(|i| char::from_u32(i as u32 + 97).unwrap())
.collect::<String>()
}
#[test]
fn test_permute_tuple_struct() {
fn assert_positional_default_split_at<T: Clone>(
permutation: &[PermutedItem<T>],
split: usize,
) {
let (positional, default) = permutation.split_at(split);
assert!(positional
.iter()
.all(|f| matches!(f, PermutedItem::Positional(_))));
assert!(default
.iter()
.all(|f| matches!(f, PermutedItem::Default(_))));
}
let positional = vec!["a", "b"];
let defaults = vec!["c", "d"];
let permutations = permute_tuple_struct(positional, defaults);
assert_eq!(permutations.len(), 3);
assert_positional_default_split_at(&permutations[0], 2);
assert_positional_default_split_at(&permutations[1], 3);
assert_positional_default_split_at(&permutations[2], 4);
}
#[test]
fn test_permute_inner_named() {
let items = vec!["a", "b", "c", "d"];
let permutations = permute_named(&items);
assert_eq!(permutations.len(), 24);
let _ = permutations
.into_iter()
.flatten()
.map(|item| {
assert!(matches!(item, PermutedItem::Named(_)));
})
.collect::<Vec<_>>();
}
#[test]
fn test_permute_inner_named_defaults() {
let mut items = vec!["a", "b"];
let permutations = permute_named_default(&items);
assert_eq!(permutations.len(), 5);
let first = permutations.first().unwrap();
assert!(first
.iter()
.all(|item| matches!(item, PermutedItem::Named(_))));
assert_eq!(
*first,
vec![PermutedItem::Named("a"), PermutedItem::Named("b"),]
);
items.clear();
let permutations = permute_pos_default(&items);
assert!(permutations.is_empty());
}
#[test]
fn test_permute_inner_named_defaults_9() {
let items = (0..9).map(idx_to_str).collect::<Vec<_>>();
for i in 1..=9 {
let inputs = &items[..i];
let permutations = permute_named_default(inputs);
let first = permutations.first().unwrap();
println!("9 defaults: {} branches", permutations.len());
println!("first item: {:?}", first);
let expected = inputs
.iter()
.map(|item| PermutedItem::Named(item.to_owned()))
.collect::<Vec<_>>();
assert_eq!(
expected, *first,
"first permutation must have the same order as the input"
);
}
}
#[test]
fn test_permute_inner_positional_defaults() {
let items = vec!["a", "b", "c"];
let permutations = permute_pos_default(&items);
assert_eq!(permutations.len(), 8);
let first = permutations.first().unwrap();
assert!(matches!(first[0], PermutedItem::Positional(_)));
assert!(matches!(first[1], PermutedItem::Named(_)));
assert!(matches!(first[2], PermutedItem::Named(_)));
assert_eq!(
*first,
vec![
PermutedItem::Positional("a"),
PermutedItem::Named("b"),
PermutedItem::Named("c")
]
);
}
#[test]
fn test_permute_positional_named() {
let items = vec!["a", "b", "c", "d"];
let permutations = permute(items, vec![]);
let first_perm = permutations.first().unwrap();
assert!(
first_perm
.0
.iter()
.all(|item| matches!(item, PermutedItem::Named(_))),
"first permutation must contain all named parameters"
);
assert!(
first_perm
.1
.iter()
.all(|item| matches!(item, PermutedItem::Named(_))),
"first permutation must contain all named parameters"
);
let full_perm = [first_perm.0.clone(), first_perm.1.clone()].concat();
assert_eq!(
full_perm,
vec![
PermutedItem::Named("a"),
PermutedItem::Named("b"),
PermutedItem::Named("c"),
PermutedItem::Named("d"),
],
"first permutation must maintain the initial order"
);
assert_eq!(permutations.len(), 34);
}
#[test]
fn test_permute_positional_default() {
let items = vec!["a", "b", "c", "d"];
let defaults = vec!["e", "f"];
let permutations = permute(items, defaults);
let first_perm = permutations.first().unwrap();
assert!(
first_perm
.0
.iter()
.all(|item| matches!(item, PermutedItem::Named(_))),
"first permutation must contain all named parameters"
);
assert!(
first_perm
.1
.iter()
.all(|item| matches!(item, PermutedItem::Named(_))),
"first permutation must contain all named parameters"
);
let full_perm = [first_perm.0.clone(), first_perm.1.clone()].concat();
assert_eq!(
full_perm,
vec![
PermutedItem::Named("a"),
PermutedItem::Named("b"),
PermutedItem::Named("c"),
PermutedItem::Named("d"),
PermutedItem::Named("e"),
PermutedItem::Named("f"),
],
"first permutation must maintain the initial order"
);
assert_eq!(permutations.len(), 34 * 5 + 3);
}
#[test]
fn test_permute_9_positional() {
for i in 1..=9 {
let items = (0..i).map(idx_to_str).collect::<Vec<_>>();
let permutations = permute(items.clone(), vec![]);
println!("{} positionals: {} branches", i, permutations.len());
let first_perm = permutations.first().unwrap();
assert_eq!(
first_perm.0,
items.clone()
.into_iter()
.map(|item| PermutedItem::Named(item.to_owned()))
.collect::<Vec<_>>(),
"permutation for {} positional params: first permutation must have the same order as the input",
i
);
}
}
#[test]
fn test_permute_9_default() {
for i in 1..=9 {
let items = (0..i).map(idx_to_str).collect::<Vec<_>>();
let permutations = permute(vec![], items.clone());
println!("{} defaults: {} branches", i, permutations.len());
let first_perm = permutations.first().unwrap();
assert_eq!(
first_perm.1,
items.clone()
.into_iter()
.map(|item| PermutedItem::Default(item.to_owned()))
.collect::<Vec<_>>(),
"permutation for {} default params: first permutation must have the same order as the input",
i
);
}
}
#[test]
fn test_permute_9_default_positional() {
const NUM: usize = 9;
let items = (0..NUM).map(idx_to_str).collect::<Vec<_>>();
for i in 0..=NUM {
let (pos, def) = items.split_at(i);
let permutations = permute(pos.to_vec(), def.to_vec());
println!(
"pos: {}, def: {}\tbranches: {}",
pos.len(),
def.len(),
permutations.len()
);
let first_perm = permutations.first().unwrap();
assert_eq!(
first_perm.0,
pos.iter()
.map(|item| PermutedItem::Named(item.to_owned()))
.collect::<Vec<_>>(),
"pos: {}, def: {}: first permutation must have the same order as the input",
pos.len(),
def.len()
);
}
}
}