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 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}