1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::constraints::{resolve_2, Constraint, ResolveFn, VarWatch};
use crate::goals::{unify, Goal};
use crate::lvec::LVec;
use crate::{State, Unify, Value};
use std::fmt::Debug;
use std::ops::Range;
use std::rc::Rc;

/// Create a [`Goal`] that attempts to unify an `LVec<T>` with
/// a slice from another `LVec<T>` defined by a ['Range'].
///
/// # Examples:
/// ```
/// use canrun::{all, unify, lvec, LVar, Query};
///
/// let needle = LVar::new();
/// let haystack = LVar::new();
/// let range = LVar::new();
/// let goal = all![
///     unify(&range, 0..2),
///     unify(&haystack, lvec![1, 2, 3]),
///     lvec::slice(&needle, &range, &haystack),
/// ];
/// let results: Vec<_> = goal.query(needle).collect();
/// assert_eq!(results, vec![vec![1, 2]]);
/// ```
pub fn slice<T, SV, RV, CV>(slice: SV, range: RV, collection: CV) -> Slice<T>
where
    T: Unify,
    LVec<T>: Unify,
    SV: Into<Value<LVec<T>>>,
    RV: Into<Value<Range<usize>>>,
    CV: Into<Value<LVec<T>>>,
{
    Slice {
        slice: slice.into(),
        range: range.into(),
        collection: collection.into(),
    }
}

/// A [`Goal`] that attempts to unify an `LVec<T>` with
/// a slice from another `LVec<T>` defined by a ['Range']. Create with [`slice()`].
#[derive(Debug)]
pub struct Slice<T: Unify> {
    slice: Value<LVec<T>>,
    range: Value<Range<usize>>,
    collection: Value<LVec<T>>,
}

impl<T: Unify> Clone for Slice<T> {
    fn clone(&self) -> Self {
        Self {
            slice: self.slice.clone(),
            range: self.range.clone(),
            collection: self.collection.clone(),
        }
    }
}

impl<T: Unify> Goal for Slice<T> {
    fn apply(&self, state: State) -> Option<State> {
        state.constrain(Rc::new(self.clone()))
    }
}

impl<T: Unify> Constraint for Slice<T>
where
    T: Unify,
{
    fn attempt(&self, state: &State) -> Result<ResolveFn, VarWatch> {
        let (range, collection) = resolve_2(&self.range, &self.collection, state)?;
        let slice_a = self.slice.clone();
        let slice_b = collection.vec.get((*range).clone());
        match slice_b {
            Some(slice_b) => {
                let slice_b: LVec<_> = slice_b.into();
                Ok(Box::new(move |state| unify(slice_a, slice_b).apply(state)))
            }
            None => Ok(Box::new(|_| None)), // need a better way to fail
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::goal_vec;
    use crate::goals::unify;
    use crate::{lvec, LVar};

    #[test]
    fn basic_slice() {
        let x = LVar::new();
        let goals = goal_vec![lvec::slice(lvec![x], 0..1, lvec![1, 2, 3])];
        goals.assert_permutations_resolve_to(x, vec![1]);
    }

    #[test]
    fn slice_with_lh_var() {
        let x = LVar::new();
        let goals = goal_vec![unify(x, 3), lvec::slice(lvec![2, x], 1..3, lvec![1, 2, 3])];
        goals.assert_permutations_resolve_to(x, vec![3]);
    }

    #[test]
    fn slice_with_rh_var() {
        let x = LVar::new();
        let goals = goal_vec![unify(x, 3), lvec::slice(lvec![2, 3], 1..3, lvec![1, 2, x])];
        goals.assert_permutations_resolve_to(x, vec![3]);
    }

    #[test]
    fn slice_with_out_of_range() {
        let x = LVar::new();
        let goals = goal_vec![unify(x, 3), lvec::slice(lvec![2, 3], 1..4, lvec![1, 2, x])];
        // Is simply failing the goal what we want here?
        goals.assert_permutations_resolve_to(x, vec![]);
    }

    #[test]
    fn debug_impl() {
        let slice = lvec::slice(lvec![2, 3], 1..4, lvec![1, 2, 3]);
        assert_ne!(format!("{slice:?}"), "");
    }
}