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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2018 Eduardo Sánchez Muñoz
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use crate::Node;

/// Base struct from which `Builder` are created.
/// See `Builder` example.
pub struct BuilderBase {
    stack: Vec<Vec<Node>>,
    current: Vec<Node>,
}

/// Helper struct to build SISE trees and get index paths
/// of the inserted nodes.
///
/// # Example
///
/// ```
/// use sise::sise_expr;
///
/// let mut builder_base = sise::BuilderBase::new();
/// let mut builder = builder_base.builder();
///
/// // Build (atom-1 atom-2 (atom-3 atom-4) atom-5)
/// builder.add_node("atom-1");
/// assert_eq!(builder.last_index_path(), [0]);
/// builder.add_node("atom-2");
/// assert_eq!(builder.last_index_path(), [1]);
/// builder.begin_list();
/// builder.add_node("atom-3");
/// assert_eq!(builder.last_index_path(), [2, 0]);
/// builder.add_node("atom-4");
/// assert_eq!(builder.last_index_path(), [2, 1]);
/// builder.end_list();
/// assert_eq!(builder.last_index_path(), [2]);
/// builder.add_node("atom-5");
/// assert_eq!(builder.last_index_path(), [3]);
/// builder.finish();
///
/// let root_node = builder_base.into_node();
/// let expected = sise_expr!(["atom-1", "atom-2", ["atom-3", "atom-4"], "atom-5"]);
/// assert_eq!(root_node, expected);
/// ```
pub struct Builder<'a> {
    base: &'a mut BuilderBase,
    min_depth: usize,
}

impl BuilderBase {
    #[inline]
    pub fn new() -> Self {
        Self {
            stack: Vec::new(),
            current: Vec::new(),
        }
    }

    #[inline]
    pub fn builder(&mut self) -> Builder {
        assert!(self.stack.is_empty());
        assert!(self.current.is_empty());
        Builder {
            base: self,
            min_depth: 0,
        }
    }

    #[inline]
    pub fn into_node(self) -> Node {
        assert!(self.stack.is_empty());
        Node::List(self.current)
    }
}

impl Default for BuilderBase {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<'a> Builder<'a> {
    /// Returns the index path of the last inserted node.
    pub fn last_index_path(&self) -> Vec<usize> {
        let mut path = Vec::with_capacity(self.base.stack.len() + 1);
        for stack_item in self.base.stack.iter() {
            path.push(stack_item.len());
        }
        if !self.base.current.is_empty() {
            path.push(self.base.current.len() - 1);
        }
        path
    }

    /// Creates a builder that won't allow to pop further.
    ///
    /// # Example
    ///
    /// ```
    /// let r = std::panic::catch_unwind(|| {
    ///     let mut builder_base = sise::BuilderBase::new();
    ///     let mut builder = builder_base.builder();
    ///
    ///     builder.begin_list();
    ///     let mut builder2 = builder.sub_builder();
    ///     builder2.end_list();
    /// });
    /// assert!(r.is_err());
    /// ```
    #[inline]
    pub fn sub_builder<'b>(&'b mut self) -> Builder<'b> {
        let min_depth = self.base.stack.len();
        Builder {
            base: self.base,
            min_depth: min_depth,
        }
    }

    /// Adds `node` into the current list.
    pub fn add_node<T: Into<Node>>(&mut self, node: T) {
        self.base.current.push(node.into());
    }

    /// Creates a new list, pushing the current one into a stack.
    /// This new list will be pushed into the current one.
    pub fn begin_list(&mut self) {
        self.base.stack.push(std::mem::replace(&mut self.base.current, Vec::new()));
    }

    /// Finishes the current list, popping a list from the
    /// stack and setting it as current.
    pub fn end_list(&mut self) {
        assert!(self.base.stack.len() > self.min_depth);
        let parent_list = self.base.stack.pop().unwrap();
        let current_list = std::mem::replace(&mut self.base.current, parent_list);
        self.base.current.push(Node::List(current_list));
    }

    /// Finishes the builder, making sure that the stack depth
    /// is the same as when it was created.
    pub fn finish(self) {
        assert_eq!(self.base.stack.len(), self.min_depth);
    }
}