rpn_cli/core/
interface.rs

1use crate::calc::context::Context;
2use crate::calc::undo::Undo;
3use crate::calc::value::{Value, ValueRef};
4use crate::core::action::Action;
5use crate::core::count::Count;
6use crate::core::helper::{CommandEditor, CommandHelper};
7use crate::core::stack::ValueStack;
8use crate::engine::Engine;
9use crate::error::MyResult;
10use itertools::Itertools;
11use rustyline::config::Configurer;
12use rustyline::CompletionType;
13use std::cell::RefCell;
14use std::collections::{BTreeMap, BTreeSet, HashMap};
15use std::io::Write;
16use std::rc::Rc;
17use std::time::SystemTime;
18
19pub enum Operation<W: Write> {
20    ValueNone(fn() -> MyResult<ValueRef>),
21    ValueOne(fn(ValueRef) -> MyResult<ValueRef>),
22    ValueTwo(fn(ValueRef, ValueRef) -> MyResult<ValueRef>),
23    ValueAll(fn(Vec<ValueRef>) -> MyResult<ValueRef>),
24    SeriesTwo(fn(ValueRef, ValueRef) -> MyResult<Vec<ValueRef>>),
25    SeriesThree(fn(ValueRef, ValueRef, ValueRef) -> MyResult<Vec<ValueRef>>),
26    SeriesAll(fn(Vec<ValueRef>) -> MyResult<Vec<ValueRef>>),
27    TimeNow(fn(&Option<SystemTime>) -> MyResult<ValueRef>),
28    TimeCast(fn(ValueRef) -> MyResult<ValueRef>),
29    ContextNone(fn(&mut Context)),
30    ContextOne(fn(&mut Context, ValueRef)),
31    EngineNone(fn(&mut Engine<W>, &mut W, &mut ValueStack) -> MyResult<Option<bool>>),
32    EngineUndo(fn(&mut Engine<W>, &mut ValueStack, &mut Undo, &mut Undo, Option<&str>) -> MyResult<Option<bool>>, Count, Count),
33}
34
35impl<W: Write> Operation<W> {
36    pub fn count_parameters(&self) -> (Count, Count) {
37        match self {
38            Self::ValueNone(_) => (Count::N(0), Count::N(1)),
39            Self::ValueOne(_) => (Count::N(1), Count::N(1)),
40            Self::ValueTwo(_) => (Count::N(2), Count::N(1)),
41            Self::ValueAll(_) => (Count::All, Count::N(1)),
42            Self::SeriesTwo(_) => (Count::N(2), Count::All),
43            Self::SeriesThree(_) => (Count::N(3), Count::All),
44            Self::SeriesAll(_) => (Count::All, Count::All),
45            Self::TimeNow(_) => (Count::N(0), Count::N(1)),
46            Self::TimeCast(_) => (Count::N(1), Count::N(1)),
47            Self::ContextNone(_) => (Count::N(0), Count::N(0)),
48            Self::ContextOne(_) => (Count::N(1), Count::N(0)),
49            Self::EngineNone(_) => (Count::N(0), Count::N(0)),
50            Self::EngineUndo(_, input, output) => (*input, *output),
51        }
52    }
53}
54
55#[derive(Clone, Copy, Debug, PartialEq)]
56pub enum Completion {
57    Keyword,
58    Filename,
59}
60
61pub enum Directive<W: Write> {
62    EngineOne(fn(&mut Engine<W>, &mut W, &mut ValueStack, Option<String>) -> MyResult<bool>, Completion),
63    EngineAll(fn(&mut Engine<W>, &mut W, &mut ValueStack, Vec<String>) -> MyResult<bool>, Completion),
64    EngineApply(fn(&mut Engine<W>, &mut W, Option<&str>, &mut ValueStack, &mut Undo, &mut Undo, Vec<String>) -> MyResult<bool>, Completion),
65}
66
67pub enum Description {
68    Separator(String),
69    Operation((String, Count, Count, String)),
70    Directive((String, String)),
71}
72
73pub type Operations<W> = HashMap<String, Rc<Operation<W>>>;
74pub type Directives<W> = HashMap<String, Rc<Directive<W>>>;
75pub type Definitions<W> = BTreeMap<String, (Vec<Action<W>>, Count, Count, String)>;
76pub type Descriptions = Vec<Description>;
77
78pub struct Interface<W: Write> {
79    operations: Operations<W>,
80    directives: Directives<W>,
81    definitions: Definitions<W>,
82    variables: HashMap<String, ValueRef>,
83    descriptions: Descriptions,
84}
85
86impl<W: Write> Interface<W> {
87    pub fn new() -> Self {
88        let operations = Operations::new();
89        let directives = Directives::new();
90        let definitions = Definitions::new();
91        let variables = HashMap::new();
92        let descriptions = Descriptions::new();
93        Self { operations, directives, definitions, variables, descriptions }
94    }
95
96    pub fn build() -> Self {
97        let interface = Self::new();
98        let interface = interface
99            .with_separator("Arithmetic operations")
100            .with_operation(vec!["add", "+"], Operation::ValueTwo(Value::calc_add), "Add two values")
101            .with_operation(vec!["sub", "-"], Operation::ValueTwo(Value::calc_sub), "Subtract two values")
102            .with_operation(vec!["mul", "*"], Operation::ValueTwo(Value::calc_mul), "Multiply two values")
103            .with_operation(vec!["div", "/"], Operation::ValueTwo(Value::calc_div), "Divide two values")
104            .with_operation(vec!["mod", "%"], Operation::ValueTwo(Value::calc_mod), "Modulo two values")
105            .with_operation(vec!["neg"], Operation::ValueOne(Value::calc_neg), "Find the negative")
106            .with_operation(vec!["inv"], Operation::ValueOne(Value::calc_inv), "Find the inverse")
107            .with_operation(vec!["pow"], Operation::ValueTwo(Value::calc_pow), "Raise to the power")
108            .with_operation(vec!["sqrt"], Operation::ValueOne(Value::calc_sqrt), "Find the square root")
109            .with_operation(vec!["sum"], Operation::ValueAll(Value::calc_sum), "Sum all values")
110            .with_operation(vec!["prod"], Operation::ValueAll(Value::calc_prod), "Multiply all values");
111        let interface = interface
112            .with_separator("Sequence operations")
113            .with_operation(vec!["seq"], Operation::SeriesTwo(Value::calc_seq), "Generate integer sequence (start to stop)")
114            .with_operation(vec!["step"], Operation::SeriesThree(Value::calc_step), "Generate integer sequence (start with step to stop)")
115            .with_operation(vec!["sort"], Operation::SeriesAll(Value::sort_seq), "Sort stack or sequence")
116            .with_operation(vec!["rev"], Operation::SeriesAll(Value::rev_seq), "Reverse stack or sequence")
117            .with_operation(vec!["flat"], Operation::EngineUndo(Engine::flat_values, Count::All, Count::All), "Flatten entire stack");
118        let interface = interface
119            .with_separator("Bitwise operations")
120            .with_operation(vec!["and"], Operation::ValueTwo(Value::calc_and), "Bitwise AND two values")
121            .with_operation(vec!["or"], Operation::ValueTwo(Value::calc_or), "Bitwise OR two values")
122            .with_operation(vec!["xor"], Operation::ValueTwo(Value::calc_xor), "Bitwise XOR two values")
123            .with_operation(vec!["shl"], Operation::ValueTwo(Value::calc_shl), "Shift left (multiply by power of 2)")
124            .with_operation(vec!["shr"], Operation::ValueTwo(Value::calc_shr), "Shift right (divide by power of 2)");
125        let interface = interface
126            .with_separator("Time operations")
127            .with_operation(vec!["now"], Operation::TimeNow(Value::calc_now), "Get the current time (in UTC)")
128            .with_operation(vec!["plain"], Operation::TimeCast(Value::cast_plain), "Format as a plain value")
129            .with_operation(vec!["delta"], Operation::TimeCast(Value::cast_delta), "Format as a delta value (duration)")
130            .with_operation(vec!["time"], Operation::TimeCast(Value::cast_time), "Format as a time value (in UTC)");
131        let interface = interface
132            .with_separator("Formatting commands")
133            .with_operation(vec!["dec"], Operation::ContextNone(Context::set_dec), "Format as decimal values")
134            .with_operation(vec!["hex"], Operation::ContextNone(Context::set_hex), "Format as hexadecimal values")
135            .with_operation(vec!["sep"], Operation::ContextNone(Context::set_sep), "Include comma separators")
136            .with_operation(vec!["nosep"], Operation::ContextNone(Context::no_sep), "Include no separators")
137            .with_operation(vec!["dp"], Operation::ContextOne(Context::set_dp), "Use fixed decimal places")
138            .with_operation(vec!["nodp"], Operation::ContextNone(Context::no_dp), "Use free decimal places");
139        let interface = interface
140            .with_separator("Stack commands")
141            .with_operation(vec!["clear", "c"], Operation::EngineUndo(Engine::clear_values, Count::All, Count::N(0)), "Remove all values from the stack")
142            .with_operation(vec!["pop", "p"], Operation::EngineUndo(Engine::pop_value, Count::N(1), Count::N(0)), "Remove a value from the stack")
143            .with_operation(vec!["dup", "d"], Operation::EngineUndo(Engine::dup_value, Count::N(1), Count::N(2)), "Duplicate a value on the stack")
144            .with_operation(vec!["swap", "s"], Operation::EngineUndo(Engine::swap_values, Count::N(2), Count::N(2)), "Swap two values on the stack")
145            .with_operation(vec!["cut"], Operation::EngineUndo(Engine::cut_value, Count::N(1), Count::N(0)), "Cut a value to the internal clipboard")
146            .with_operation(vec!["copy"], Operation::EngineUndo(Engine::copy_value, Count::N(1), Count::N(1)), "Copy a value to the internal clipboard")
147            .with_operation(vec!["paste"], Operation::EngineUndo(Engine::paste_value, Count::N(0), Count::N(1)), "Paste a value from the internal clipboard");
148        let interface = interface
149            .with_separator("History commands")
150            .with_operation(vec!["undo", "u"], Operation::EngineUndo(Engine::undo_stack, Count::N(0), Count::N(0)), "Undo the last operation")
151            .with_operation(vec!["redo", "r"], Operation::EngineUndo(Engine::redo_stack, Count::N(0), Count::N(0)), "Redo the next operation")
152            .with_operation(vec!["hist", "h"], Operation::EngineNone(Engine::show_history), "Show all undo/redo history");
153        let interface = interface
154            .with_separator("General directives")
155            .with_directive(vec!["import"], Directive::EngineAll(Engine::import_file, Completion::Filename), "Import file e.g. \"import file.txt\"")
156            .with_directive(vec!["export"], Directive::EngineAll(Engine::export_file, Completion::Filename), "Export file e.g. \"export file.txt\"")
157            .with_directive(vec!["set", "="], Directive::EngineOne(Engine::set_variable, Completion::Keyword), "Set variable, e.g. \"set x\"")
158            .with_directive(vec!["define"], Directive::EngineAll(Engine::define_function, Completion::Keyword), "Define function e.g. \"define cube 3 pow\"")
159            .with_directive(vec!["apply"], Directive::EngineApply(Engine::apply_all, Completion::Keyword), "Apply to stack or sequence, e.g. \"apply 3 pow\"");
160        let interface = interface
161            .with_separator("General commands")
162            .with_operation(vec!["show"], Operation::EngineNone(Engine::show_stack), "Show all values on the stack")
163            .with_operation(vec!["help"], Operation::EngineNone(Engine::show_help), "Show this help text");
164        interface
165    }
166
167    fn with_separator(mut self, description: &str) -> Self {
168        self.descriptions.push(Description::Separator(String::from(description)));
169        self
170    }
171
172    fn with_operation(
173        mut self,
174        keywords: Vec<&str>,
175        operation: Operation<W>,
176        description: &str,
177    ) -> Self {
178        let operation = Rc::new(operation);
179        for keyword in keywords.iter() {
180            self.operations.insert(keyword.to_string(), Rc::clone(&operation));
181        }
182        let keywords = Self::describe_keywords(keywords);
183        let (input, output) = operation.count_parameters();
184        self.descriptions.push(Description::Operation((
185            keywords,
186            input,
187            output,
188            String::from(description),
189        )));
190        self
191    }
192
193    fn with_directive(
194        mut self,
195        keywords: Vec<&str>,
196        directive: Directive<W>,
197        description: &str,
198    ) -> Self {
199        let directive = Rc::new(directive);
200        for keyword in keywords.iter() {
201            self.directives.insert(keyword.to_string(), Rc::clone(&directive));
202        }
203        let keyword = Self::describe_keywords(keywords);
204        self.descriptions.push(Description::Directive((
205            format!("{keyword}..."),
206            String::from(description),
207        )));
208        self
209    }
210
211    #[cfg(test)]
212    fn with_definition(
213        mut self,
214        keyword: &str,
215        actions: Vec<Action<W>>,
216        input: Count,
217        output: Count,
218        description: String,
219    ) -> Self {
220        self.insert_definition(keyword, actions, input, output, description);
221        self
222    }
223
224    pub fn insert_definition(
225        &mut self,
226        keyword: &str,
227        actions: Vec<Action<W>>,
228        input: Count,
229        output: Count,
230        description: String,
231    ) {
232        self.definitions.insert(keyword.to_string(), (actions, input, output, description));
233    }
234
235    pub fn remove_definition(&mut self, keyword: &str) {
236        self.definitions.remove(keyword);
237    }
238
239    pub fn insert_variable(&mut self, keyword: String, value: ValueRef) -> bool {
240        self.variables.insert(keyword, value).is_some()
241    }
242
243    pub fn remove_variable(&mut self, keyword: &str) {
244        self.variables.remove(keyword);
245    }
246
247    pub fn get_variable(&self, keyword: &str) -> Option<ValueRef> {
248        if let Some(value) = self.variables.get(keyword) {
249            let value = value.as_ref().borrow().clone();
250            return Some(Rc::new(RefCell::new(value)));
251        }
252        None
253    }
254
255    pub fn get_variables(&self) -> &HashMap<String, ValueRef> {
256        &self.variables
257    }
258
259    fn describe_keywords(keywords: Vec<&str>) -> String {
260        if keywords.len() == 2 {
261            let primary = keywords[0];
262            let secondary = keywords[1];
263            if primary.starts_with(secondary) {
264                let chop = secondary.len();
265                return format!("{}({})", secondary, &primary[chop..]);
266            }
267        }
268        keywords.iter().map(|x| x.to_string()).join(",")
269    }
270
271    pub fn create_editor(&self) -> MyResult<CommandEditor> {
272        let mut editor = CommandEditor::new()?;
273        editor.set_helper(Some(CommandHelper::new()));
274        editor.set_completion_type(CompletionType::List);
275        self.adjust_editor(&mut editor);
276        Ok(editor)
277    }
278
279    pub fn adjust_editor(&self, editor: &mut CommandEditor) {
280        if let Some(helper) = editor.helper_mut() {
281            helper.set_commands(self.get_commands());
282            helper.set_completions(self.get_completions());
283        }
284    }
285
286    fn get_commands(&self) -> Vec<String> {
287        let commands = self.operations.keys()
288            .chain(self.directives.keys())
289            .chain(self.definitions.keys())
290            .chain(self.variables.keys())
291            .map(String::clone)
292            .collect::<BTreeSet<String>>();
293        commands.into_iter().collect::<Vec<String>>()
294    }
295
296    fn get_completions(&self) -> HashMap<String, Completion> {
297        let mut directives = HashMap::new();
298        for (keyword, directive) in self.directives.iter() {
299            match directive.as_ref() {
300                Directive::EngineOne(_, completion) => {
301                    directives.insert(String::from(keyword), *completion);
302                }
303                Directive::EngineAll(_, completion) => {
304                    directives.insert(String::from(keyword), *completion);
305                }
306                Directive::EngineApply(_, completion) => {
307                    directives.insert(String::from(keyword), *completion);
308                }
309            }
310        }
311        directives
312    }
313
314    pub fn get_operation(&self, keyword: &str) -> Option<Rc<Operation<W>>> {
315        self.operations.get(keyword).map(Rc::clone)
316    }
317
318    pub fn get_directive(&self, keyword: &str) -> Option<Rc<Directive<W>>> {
319        self.directives.get(keyword).map(Rc::clone)
320    }
321
322    pub fn get_definition(&self, keyword: &str) -> Option<Vec<Action<W>>> {
323        self.definitions.get(keyword).map(|(x, _, _, _)| x).map(Vec::clone)
324    }
325
326    // +---------------------- indent (2 chars)
327    // | +-------------------- input (5 chars)
328    // | |    +--------------- keyword (8 chars)
329    // | |    |       +------- output (5 chars)
330    // | |    |       |    +-- description
331    // v-v----v-------v----v--
332    //   N N  s(wap)  N N  Swap two values on the stack
333
334    pub fn show_help(&self, writer: &mut W, interact: bool) -> MyResult<()> {
335        let indent = if interact { "  " } else { "" };
336        for description in self.descriptions.iter() {
337            match description {
338                Description::Separator(description) => {
339                    writeln!(writer, "{}{}:", indent, description)?;
340                }
341                Description::Operation((keyword, input, output, description)) => {
342                    writeln!(writer, "{}  {:>3}  {:7} {:3}  {}", indent, input, keyword, output, description)?;
343                }
344                Description::Directive((keyword, description)) => {
345                    writeln!(writer, "{}  {:>3}  {:12} {}", indent, "", keyword, description)?;
346                }
347            }
348        }
349        self.show_definitions(writer, interact)?;
350        Ok(())
351    }
352
353    pub fn show_definitions(&self, writer: &mut W, interact: bool) -> MyResult<()> {
354        let indent = if interact { "  " } else { "" };
355        if !self.definitions.is_empty() {
356            writeln!(writer, "{}Defined functions:", indent)?;
357            for (keyword, (_, input, output, description)) in self.definitions.iter() {
358                if let Count::N(0) = output {
359                    writeln!(writer, "{}  {:>3}  {:12} Function \"{}\"", indent, input, keyword, description)?;
360                } else {
361                    writeln!(writer, "{}  {:>3}  {:7} {:3}  Function \"{}\"", indent, input, keyword, output, description)?;
362                }
363            }
364        }
365        Ok(())
366    }
367}
368
369#[cfg(test)]
370pub mod tests {
371    use crate::calc::value::{Value, ValueRef};
372    use crate::core::count::Count;
373    use crate::core::interface::{Completion, Directive, Interface, Operation};
374    use crate::core::stack::ValueStack;
375    use crate::engine::Engine;
376    use crate::error::MyResult;
377    use crate::util::text::tests::BufferWriter;
378    use pretty_assertions::assert_eq;
379    use std::cell::RefCell;
380    use std::collections::HashMap;
381    use std::rc::Rc;
382
383    #[test]
384    fn test_completes_commands() {
385        let expected_commands = vec![
386            "eight",
387            "five",
388            "four",
389            "nine",
390            "one",
391            "seven",
392            "six",
393            "ten",
394            "three",
395            "two",
396        ];
397        let expected_completions = HashMap::from([
398            (String::from("five"), Completion::Filename),
399            (String::from("six"), Completion::Filename),
400            (String::from("seven"), Completion::Filename),
401            (String::from("eight"), Completion::Keyword),
402        ]);
403        let interface = Interface::<BufferWriter>::new()
404            .with_operation(vec!["one", "two"], Operation::ValueNone(dummy_nullary), "")
405            .with_operation(vec!["three"], Operation::ValueNone(dummy_nullary), "")
406            .with_operation(vec!["four"], Operation::ValueNone(dummy_nullary), "")
407            .with_directive(vec!["five", "six"], Directive::EngineAll(dummy_directive, Completion::Filename), "")
408            .with_directive(vec!["seven"], Directive::EngineAll(dummy_directive, Completion::Filename), "")
409            .with_directive(vec!["eight"], Directive::EngineAll(dummy_directive, Completion::Keyword), "")
410            .with_definition("nine", vec![], Count::N(0), Count::N(0), String::from(""))
411            .with_definition("ten", vec![], Count::N(0), Count::N(0), String::from(""));
412        assert_eq!(interface.get_commands(), expected_commands);
413        assert_eq!(interface.get_completions(), expected_completions);
414    }
415
416    pub fn dummy_nullary() -> MyResult<ValueRef> {
417        Ok(Rc::new(RefCell::new(Value::new(None))))
418    }
419
420    pub fn dummy_unary(_: ValueRef) -> MyResult<ValueRef> {
421        Ok(Rc::new(RefCell::new(Value::new(None))))
422    }
423
424    pub fn dummy_binary(_: ValueRef, _: ValueRef) -> MyResult<ValueRef> {
425        Ok(Rc::new(RefCell::new(Value::new(None))))
426    }
427
428    pub fn dummy_series(_: Vec<ValueRef>) -> MyResult<ValueRef> {
429        Ok(Rc::new(RefCell::new(Value::new(None))))
430    }
431
432    pub fn dummy_binary_series(_: ValueRef, _: ValueRef) -> MyResult<Vec<ValueRef>> {
433        Ok(Vec::new())
434    }
435
436    pub fn dummy_series_series(_: Vec<ValueRef>) -> MyResult<Vec<ValueRef>> {
437        Ok(Vec::new())
438    }
439
440    pub fn dummy_directive(
441        _engine: &mut Engine<BufferWriter>,
442        _writer: &mut BufferWriter,
443        _stack: &mut ValueStack,
444        _tokens: Vec<String>,
445    ) -> MyResult<bool> {
446        Ok(false)
447    }
448
449    #[test]
450    fn test_shows_help_text() {
451        let expected = "\
452Arithmetic operations:
453  N N  add,+   N    Add two values
454  N N  sub,-   N    Subtract two values
455  N N  mul,*   N    Multiply two values
456  N N  div,/   N    Divide two values
457  N N  mod,%   N    Modulo two values
458    N  neg     N    Find the negative
459    N  inv     N    Find the inverse
460  N N  pow     N    Raise to the power
461    N  sqrt    N    Find the square root
462    *  sum     N    Sum all values
463    *  prod    N    Multiply all values
464Sequence operations:
465  N N  seq     *    Generate integer sequence (start to stop)
466    3  step    *    Generate integer sequence (start with step to stop)
467    *  sort    *    Sort stack or sequence
468    *  rev     *    Reverse stack or sequence
469    *  flat    *    Flatten entire stack
470Bitwise operations:
471  N N  and     N    Bitwise AND two values
472  N N  or      N    Bitwise OR two values
473  N N  xor     N    Bitwise XOR two values
474  N N  shl     N    Shift left (multiply by power of 2)
475  N N  shr     N    Shift right (divide by power of 2)
476Time operations:
477       now     N    Get the current time (in UTC)
478    N  plain   N    Format as a plain value
479    N  delta   N    Format as a delta value (duration)
480    N  time    N    Format as a time value (in UTC)
481Formatting commands:
482       dec          Format as decimal values
483       hex          Format as hexadecimal values
484       sep          Include comma separators
485       nosep        Include no separators
486    N  dp           Use fixed decimal places
487       nodp         Use free decimal places
488Stack commands:
489    *  c(lear)      Remove all values from the stack
490    N  p(op)        Remove a value from the stack
491    N  d(up)   N N  Duplicate a value on the stack
492  N N  s(wap)  N N  Swap two values on the stack
493    N  cut          Cut a value to the internal clipboard
494    N  copy    N    Copy a value to the internal clipboard
495       paste   N    Paste a value from the internal clipboard
496History commands:
497       u(ndo)       Undo the last operation
498       r(edo)       Redo the next operation
499       h(ist)       Show all undo/redo history
500General directives:
501       import...    Import file e.g. \"import file.txt\"
502       export...    Export file e.g. \"export file.txt\"
503       set,=...     Set variable, e.g. \"set x\"
504       define...    Define function e.g. \"define cube 3 pow\"
505       apply...     Apply to stack or sequence, e.g. \"apply 3 pow\"
506General commands:
507       show         Show all values on the stack
508       help         Show this help text
509";
510        let interface = Interface::build();
511        let mut writer = BufferWriter::new();
512        assert!(interface.show_help(&mut writer, false).is_ok());
513        assert_eq!(writer.buffer, expected);
514    }
515
516    #[test]
517    fn test_shows_definitions_with_null() {
518        let expected = "\
519Defined functions:
520       answer  N    Function \"6 7 mul\"
521    N  cube    N    Function \"3 pow\"
522  N N  very-long-a  Function \"\"
523  N N  very-long-bb Function \"\"
524  N N  very-long-ccc Function \"\"
525  N N  very-long-dddd Function \"\"
526  N N  very-long-eeeee Function \"\"
527";
528        let mut interface = Interface::build();
529        let mut writer = BufferWriter::new();
530        interface.insert_definition("answer", vec![], Count::N(0), Count::N(1), String::from("6 7 mul"));
531        interface.insert_definition("cube", vec![], Count::N(1), Count::N(1), String::from("3 pow"));
532        interface.insert_definition("very-long-a", vec![], Count::N(2), Count::N(0), String::from(""));
533        interface.insert_definition("very-long-bb", vec![], Count::N(2), Count::N(0), String::from(""));
534        interface.insert_definition("very-long-ccc", vec![], Count::N(2), Count::N(0), String::from(""));
535        interface.insert_definition("very-long-dddd", vec![], Count::N(2), Count::N(0), String::from(""));
536        interface.insert_definition("very-long-eeeee", vec![], Count::N(2), Count::N(0), String::from(""));
537        assert!(interface.show_definitions(&mut writer, false).is_ok());
538        assert_eq!(writer.buffer, expected);
539    }
540
541    #[test]
542    fn test_shows_definitions_with_value() {
543        let expected = "\
544Defined functions:
545       answer  N    Function \"6 7 mul\"
546    N  cube    N    Function \"3 pow\"
547  N N  long-a  N N  Function \"\"
548  N N  long-bb N N  Function \"\"
549  N N  long-ccc N N  Function \"\"
550  N N  long-dddd N N  Function \"\"
551  N N  long-eeeee N N  Function \"\"
552";
553        let mut interface = Interface::build();
554        let mut writer = BufferWriter::new();
555        interface.insert_definition("answer", vec![], Count::N(0), Count::N(1), String::from("6 7 mul"));
556        interface.insert_definition("cube", vec![], Count::N(1), Count::N(1), String::from("3 pow"));
557        interface.insert_definition("long-a", vec![], Count::N(2), Count::N(2), String::from(""));
558        interface.insert_definition("long-bb", vec![], Count::N(2), Count::N(2), String::from(""));
559        interface.insert_definition("long-ccc", vec![], Count::N(2), Count::N(2), String::from(""));
560        interface.insert_definition("long-dddd", vec![], Count::N(2), Count::N(2), String::from(""));
561        interface.insert_definition("long-eeeee", vec![], Count::N(2), Count::N(2), String::from(""));
562        assert!(interface.show_definitions(&mut writer, false).is_ok());
563        assert_eq!(writer.buffer, expected);
564    }
565}