rpn_cli/core/
interface.rs

1use crate::calc::context::Context;
2use crate::calc::meaning::AreaKind::*;
3use crate::calc::meaning::DeltaKind::*;
4use crate::calc::meaning::LengthKind::*;
5use crate::calc::meaning::SpeedKind::*;
6use crate::calc::meaning::VolumeKind::*;
7use crate::calc::meaning::*;
8use crate::calc::undo::Undo;
9use crate::calc::value::{Value, ValueRef};
10use crate::core::action::Action;
11use crate::core::count::Count;
12use crate::core::helper::{CommandEditor, CommandHelper};
13use crate::core::stack::ValueStack;
14use crate::engine::Engine;
15use crate::error::MyResult;
16use itertools::Itertools;
17use rustyline::config::Configurer;
18use rustyline::CompletionType;
19use std::cell::RefCell;
20use std::collections::{BTreeMap, BTreeSet, HashMap};
21use std::io::Write;
22use std::rc::Rc;
23use std::time::SystemTime;
24
25pub enum Operation<W: Write> {
26    ValueNone(fn() -> MyResult<ValueRef>),
27    ValueOne(fn(ValueRef) -> MyResult<ValueRef>),
28    ValueTwo(fn(ValueRef, ValueRef) -> MyResult<ValueRef>),
29    ValueAll(fn(Vec<ValueRef>) -> MyResult<ValueRef>),
30    SeriesTwo(fn(ValueRef, ValueRef) -> MyResult<Vec<ValueRef>>),
31    SeriesThree(fn(ValueRef, ValueRef, ValueRef) -> MyResult<Vec<ValueRef>>),
32    SeriesAll(fn(Vec<ValueRef>) -> MyResult<Vec<ValueRef>>),
33    TimeNow(fn(&Option<SystemTime>) -> MyResult<ValueRef>),
34    TimeCast(Meaning),
35    ContextNone(fn(&mut Context)),
36    ContextOne(fn(&mut Context, ValueRef)),
37    EngineNone(fn(&mut Engine<W>, &mut W, &mut ValueStack) -> MyResult<Option<bool>>),
38    EngineUndo(fn(&mut Engine<W>, &mut ValueStack, &mut Undo, &mut Undo, Option<&str>) -> MyResult<Option<bool>>, Count, Count),
39}
40
41impl<W: Write> Operation<W> {
42    pub fn count_parameters(&self) -> (Count, Count) {
43        match self {
44            Self::ValueNone(_) => (Count::N(0), Count::N(1)),
45            Self::ValueOne(_) => (Count::N(1), Count::N(1)),
46            Self::ValueTwo(_) => (Count::N(2), Count::N(1)),
47            Self::ValueAll(_) => (Count::All, Count::N(1)),
48            Self::SeriesTwo(_) => (Count::N(2), Count::All),
49            Self::SeriesThree(_) => (Count::N(3), Count::All),
50            Self::SeriesAll(_) => (Count::All, Count::All),
51            Self::TimeNow(_) => (Count::N(0), Count::N(1)),
52            Self::TimeCast(_) => (Count::N(1), Count::N(1)),
53            Self::ContextNone(_) => (Count::N(0), Count::N(0)),
54            Self::ContextOne(_) => (Count::N(1), Count::N(0)),
55            Self::EngineNone(_) => (Count::N(0), Count::N(0)),
56            Self::EngineUndo(_, input, output) => (*input, *output),
57        }
58    }
59}
60
61#[derive(Clone, Copy, Debug, PartialEq)]
62pub enum Completion {
63    Keyword,
64    Filename,
65}
66
67pub enum Directive<W: Write> {
68    EngineOne(fn(&mut Engine<W>, &mut W, &mut ValueStack, Option<String>) -> MyResult<bool>, Completion),
69    EngineAll(fn(&mut Engine<W>, &mut W, &mut ValueStack, Vec<String>) -> MyResult<bool>, Completion),
70    EngineApply(fn(&mut Engine<W>, &mut W, Option<&str>, &mut ValueStack, &mut Undo, &mut Undo, Vec<String>) -> MyResult<bool>, Completion),
71}
72
73pub enum Description {
74    Separator(String),
75    Operation((String, Count, Count, String)),
76    Directive((String, String)),
77}
78
79pub type Operations<W> = HashMap<String, Rc<Operation<W>>>;
80pub type Meanings = HashMap<String, Meaning>;
81pub type Directives<W> = HashMap<String, Rc<Directive<W>>>;
82pub type Definitions<W> = BTreeMap<String, (Vec<Action<W>>, Count, Count, String)>;
83pub type Descriptions = Vec<Description>;
84
85pub struct Interface<W: Write> {
86    operations: Operations<W>,
87    meanings: Meanings,
88    directives: Directives<W>,
89    definitions: Definitions<W>,
90    variables: HashMap<String, ValueRef>,
91    descriptions: Descriptions,
92}
93
94impl<W: Write> Interface<W> {
95    pub fn new() -> Self {
96        let operations = Operations::new();
97        let meanings = Meanings::new();
98        let directives = Directives::new();
99        let definitions = Definitions::new();
100        let variables = HashMap::new();
101        let descriptions = Descriptions::new();
102        Self { operations, meanings, directives, definitions, variables, descriptions }
103    }
104
105    pub fn build() -> Self {
106        let interface = Self::new();
107        let interface = interface
108            .with_separator("Arithmetic operations")
109            .with_operation(vec!["add", "+"], Operation::ValueTwo(Value::calc_add), "Add two values")
110            .with_operation(vec!["sub", "-"], Operation::ValueTwo(Value::calc_sub), "Subtract two values")
111            .with_operation(vec!["mul", "*"], Operation::ValueTwo(Value::calc_mul), "Multiply two values")
112            .with_operation(vec!["div", "/"], Operation::ValueTwo(Value::calc_div), "Divide two values")
113            .with_operation(vec!["mod", "%"], Operation::ValueTwo(Value::calc_mod), "Modulo two values")
114            .with_operation(vec!["neg"], Operation::ValueOne(Value::calc_neg), "Find the negative")
115            .with_operation(vec!["inv"], Operation::ValueOne(Value::calc_inv), "Find the inverse")
116            .with_operation(vec!["pow"], Operation::ValueTwo(Value::calc_pow), "Raise to the power")
117            .with_operation(vec!["sqrt"], Operation::ValueOne(Value::calc_sqrt), "Find the square root")
118            .with_operation(vec!["sum"], Operation::ValueAll(Value::calc_sum), "Sum all values")
119            .with_operation(vec!["prod"], Operation::ValueAll(Value::calc_prod), "Multiply all values");
120        let interface = interface
121            .with_separator("Sequence operations")
122            .with_operation(vec!["seq"], Operation::SeriesTwo(Value::calc_seq), "Generate integer sequence (start to stop)")
123            .with_operation(vec!["step"], Operation::SeriesThree(Value::calc_step), "Generate integer sequence (start with step to stop)")
124            .with_operation(vec!["sort"], Operation::SeriesAll(Value::sort_seq), "Sort stack or sequence")
125            .with_operation(vec!["rev"], Operation::SeriesAll(Value::rev_seq), "Reverse stack or sequence")
126            .with_operation(vec!["flat"], Operation::EngineUndo(Engine::flat_values, Count::All, Count::All), "Flatten entire stack");
127        let interface = interface
128            .with_separator("Bitwise operations")
129            .with_operation(vec!["and"], Operation::ValueTwo(Value::calc_and), "Bitwise AND two values")
130            .with_operation(vec!["or"], Operation::ValueTwo(Value::calc_or), "Bitwise OR two values")
131            .with_operation(vec!["xor"], Operation::ValueTwo(Value::calc_xor), "Bitwise XOR two values")
132            .with_operation(vec!["shl"], Operation::ValueTwo(Value::calc_shl), "Shift left (multiply by power of 2)")
133            .with_operation(vec!["shr"], Operation::ValueTwo(Value::calc_shr), "Shift right (divide by power of 2)");
134        let interface = interface
135            .with_separator("Time operations")
136            .with_operation(vec!["now"], Operation::TimeNow(Value::calc_now), "Get the current time (in UTC)")
137            .with_operation(vec!["plain"], Operation::TimeCast(Meaning::Plain), "Format as a plain value")
138            .with_operation(vec!["delta"], Operation::TimeCast(Meaning::Delta(HMS)), "Format as a delta value (duration)")
139            .with_operation(vec!["time"], Operation::TimeCast(Meaning::Time), "Format as a time value (in UTC)");
140        let interface = interface
141            .with_separator("Formatting commands")
142            .with_operation(vec!["dec"], Operation::ContextNone(Context::set_dec), "Format as decimal values")
143            .with_operation(vec!["hex"], Operation::ContextNone(Context::set_hex), "Format as hexadecimal values")
144            .with_operation(vec!["sep"], Operation::ContextNone(Context::set_sep), "Include comma separators")
145            .with_operation(vec!["nosep"], Operation::ContextNone(Context::no_sep), "Include no separators")
146            .with_operation(vec!["dp"], Operation::ContextOne(Context::set_dp), "Use fixed decimal places")
147            .with_operation(vec!["nodp"], Operation::ContextNone(Context::no_dp), "Use free decimal places");
148        let interface = interface
149            .with_separator("Stack commands")
150            .with_operation(vec!["show"], Operation::EngineNone(Engine::show_stack), "Show all values on the stack")
151            .with_operation(vec!["clear"], Operation::EngineUndo(Engine::clear_values, Count::All, Count::N(0)), "Remove all values from the stack")
152            .with_operation(vec!["pop"], Operation::EngineUndo(Engine::pop_value, Count::N(1), Count::N(0)), "Remove a value from the stack")
153            .with_operation(vec!["dup"], Operation::EngineUndo(Engine::dup_value, Count::N(1), Count::N(2)), "Duplicate a value on the stack")
154            .with_operation(vec!["swap"], Operation::EngineUndo(Engine::swap_values, Count::N(2), Count::N(2)), "Swap two values on the stack")
155            .with_operation(vec!["cut"], Operation::EngineUndo(Engine::cut_value, Count::N(1), Count::N(0)), "Cut a value to the internal clipboard")
156            .with_operation(vec!["copy"], Operation::EngineUndo(Engine::copy_value, Count::N(1), Count::N(1)), "Copy a value to the internal clipboard")
157            .with_operation(vec!["paste"], Operation::EngineUndo(Engine::paste_value, Count::N(0), Count::N(1)), "Paste a value from the internal clipboard");
158        let interface = interface
159            .with_separator("History commands")
160            .with_operation(vec!["undo", "u"], Operation::EngineUndo(Engine::undo_stack, Count::N(0), Count::N(0)), "Undo the last operation")
161            .with_operation(vec!["redo", "r"], Operation::EngineUndo(Engine::redo_stack, Count::N(0), Count::N(0)), "Redo the next operation")
162            .with_operation(vec!["hist"], Operation::EngineNone(Engine::show_history), "Show all undo/redo history");
163        let interface = interface
164            .with_separator("General directives")
165            .with_directive(vec!["import"], Directive::EngineAll(Engine::import_file, Completion::Filename), "Import file e.g. \"import file.txt\"")
166            .with_directive(vec!["export"], Directive::EngineAll(Engine::export_file, Completion::Filename), "Export file e.g. \"export file.txt\"")
167            .with_directive(vec!["set", "="], Directive::EngineOne(Engine::set_variable, Completion::Keyword), "Set variable, e.g. \"set x\"")
168            .with_directive(vec!["define"], Directive::EngineAll(Engine::define_function, Completion::Keyword), "Define function e.g. \"define cube 3 pow\"")
169            .with_directive(vec!["apply"], Directive::EngineApply(Engine::apply_all, Completion::Keyword), "Apply to stack or sequence, e.g. \"apply 3 pow\"");
170        let interface = interface
171            .with_separator("General commands")
172            .with_operation(vec!["units"], Operation::EngineNone(Engine::show_units), "Show unit names and symbols")
173            .with_operation(vec!["help"], Operation::EngineNone(Engine::show_help), "Show this help text");
174        let meanings = Meaning::get_meanings().unwrap_or_default();
175        interface.with_meanings(meanings)
176    }
177
178    fn with_separator(mut self, description: &str) -> Self {
179        self.descriptions.push(Description::Separator(String::from(description)));
180        self
181    }
182
183    fn with_operation(
184        mut self,
185        keywords: Vec<&str>,
186        operation: Operation<W>,
187        description: &str,
188    ) -> Self {
189        let operation = Rc::new(operation);
190        for keyword in keywords.iter() {
191            self.operations.insert(keyword.to_string(), Rc::clone(&operation));
192        }
193        let keywords = Self::describe_keywords(keywords);
194        let (input, output) = operation.count_parameters();
195        self.descriptions.push(Description::Operation((
196            keywords,
197            input,
198            output,
199            String::from(description),
200        )));
201        self
202    }
203
204    fn with_meanings(
205        mut self,
206        meanings: Meanings,
207    ) -> Self {
208        self.meanings = meanings;
209        self
210    }
211
212    fn with_directive(
213        mut self,
214        keywords: Vec<&str>,
215        directive: Directive<W>,
216        description: &str,
217    ) -> Self {
218        let directive = Rc::new(directive);
219        for keyword in keywords.iter() {
220            self.directives.insert(keyword.to_string(), Rc::clone(&directive));
221        }
222        let keyword = Self::describe_keywords(keywords);
223        self.descriptions.push(Description::Directive((
224            format!("{keyword}..."),
225            String::from(description),
226        )));
227        self
228    }
229
230    #[cfg(test)]
231    fn with_definition(
232        mut self,
233        keyword: &str,
234        actions: Vec<Action<W>>,
235        input: Count,
236        output: Count,
237        description: String,
238    ) -> Self {
239        self.insert_definition(keyword, actions, input, output, description);
240        self
241    }
242
243    pub fn insert_definition(
244        &mut self,
245        keyword: &str,
246        actions: Vec<Action<W>>,
247        input: Count,
248        output: Count,
249        description: String,
250    ) {
251        self.definitions.insert(keyword.to_string(), (actions, input, output, description));
252    }
253
254    pub fn remove_definition(&mut self, keyword: &str) {
255        self.definitions.remove(keyword);
256    }
257
258    pub fn insert_variable(&mut self, keyword: String, value: ValueRef) -> bool {
259        self.variables.insert(keyword, value).is_some()
260    }
261
262    pub fn remove_variable(&mut self, keyword: &str) {
263        self.variables.remove(keyword);
264    }
265
266    pub fn get_variable(&self, keyword: &str) -> Option<ValueRef> {
267        if let Some(value) = self.variables.get(keyword) {
268            let value = value.as_ref().borrow().clone();
269            return Some(Rc::new(RefCell::new(value)));
270        }
271        None
272    }
273
274    pub fn get_variables(&self) -> &HashMap<String, ValueRef> {
275        &self.variables
276    }
277
278    fn describe_keywords(keywords: Vec<&str>) -> String {
279        if keywords.len() == 2 {
280            let primary = keywords[0];
281            let secondary = keywords[1];
282            if primary.starts_with(secondary) {
283                let chop = secondary.len();
284                return format!("{}({})", secondary, &primary[chop..]);
285            }
286        }
287        keywords.iter().map(|x| x.to_string()).join(",")
288    }
289
290    pub fn create_editor(&self) -> MyResult<CommandEditor> {
291        let mut editor = CommandEditor::new()?;
292        editor.set_helper(Some(CommandHelper::new()));
293        editor.set_completion_type(CompletionType::List);
294        self.adjust_editor(&mut editor);
295        Ok(editor)
296    }
297
298    pub fn adjust_editor(&self, editor: &mut CommandEditor) {
299        if let Some(helper) = editor.helper_mut() {
300            helper.set_commands(self.get_commands());
301            helper.set_completions(self.get_completions());
302        }
303    }
304
305    fn get_commands(&self) -> Vec<String> {
306        let commands = self.operations.keys()
307            .chain(self.meanings.keys())
308            .chain(self.directives.keys())
309            .chain(self.definitions.keys())
310            .chain(self.variables.keys())
311            .map(String::clone)
312            .collect::<BTreeSet<String>>();
313        commands.into_iter().collect::<Vec<String>>()
314    }
315
316    fn get_completions(&self) -> HashMap<String, Completion> {
317        let mut completions = HashMap::new();
318        for (keyword, directive) in self.directives.iter() {
319            match directive.as_ref() {
320                Directive::EngineOne(_, completion) => {
321                    completions.insert(String::from(keyword), *completion);
322                }
323                Directive::EngineAll(_, completion) => {
324                    completions.insert(String::from(keyword), *completion);
325                }
326                Directive::EngineApply(_, completion) => {
327                    completions.insert(String::from(keyword), *completion);
328                }
329            }
330        }
331        completions
332    }
333
334    pub fn get_operation(&self, keyword: &str) -> Option<Rc<Operation<W>>> {
335        self.operations.get(keyword).map(Rc::clone)
336    }
337
338    pub fn get_meaning(&self, keyword: &str) -> Option<Meaning> {
339        self.meanings.get(keyword).map(Meaning::clone)
340    }
341
342    pub fn get_directive(&self, keyword: &str) -> Option<Rc<Directive<W>>> {
343        self.directives.get(keyword).map(Rc::clone)
344    }
345
346    pub fn get_definition(&self, keyword: &str) -> Option<Vec<Action<W>>> {
347        self.definitions.get(keyword).map(|(x, _, _, _)| x).map(Vec::clone)
348    }
349
350    pub fn show_units(&self, writer: &mut W, interact: bool) -> MyResult<()> {
351        let indent = if interact { "  " } else { "" };
352        Self::format_prefixes(writer, indent, "Prefix")?;
353        Self::format_units::<DeltaKind, _>(writer, indent, "Time", Self::translate_generic)?;
354        Self::format_units::<LengthKind, _>(writer, indent, "Length", Self::translate_generic)?;
355        Self::format_units::<AreaKind, _>(writer, indent, "Area", Self::translate_area)?;
356        Self::format_units::<VolumeKind, _>(writer, indent, "Volume", Self::translate_volume)?;
357        Self::format_units::<SpeedKind, _>(writer, indent, "Speed", Self::translate_speed)?;
358        Self::format_units::<MassKind, _>(writer, indent, "Mass", Self::translate_generic)?;
359        Self::format_units::<TempKind, _>(writer, indent, "Temperature", Self::translate_generic)?;
360        Self::format_units::<DataKind, _>(writer, indent, "Data", Self::translate_generic)?;
361        Ok(())
362    }
363
364    fn format_prefixes(writer: &mut W, indent: &str, label: &str) -> MyResult<()> {
365        writeln!(writer, "{}{} units:", indent, label)?;
366        PrefixKind::walk_values(true, true, |prefix| {
367            let symbol = prefix.get_symbol();
368            let name = prefix.get_name();
369            let power = prefix.get_power();
370            match power {
371                0 => writeln!(writer, "{}  -    -            1", indent)?,
372                1 => writeln!(writer, "{}  {:4} {:12} 10", indent, symbol, name)?,
373                _ => writeln!(writer, "{}  {:4} {:12} 10^{}", indent, symbol, name, power)?,
374            }
375            Ok(())
376        })
377    }
378
379    fn format_units<U: UnitInfo, F: Fn(U) -> Option<(Option<String>, Option<String>)>>(
380        writer: &mut W,
381        indent: &str,
382        label: &str,
383        function: F,
384    ) -> MyResult<()> {
385        writeln!(writer, "{}{} units:", indent, label)?;
386        U::walk_values(false, false, |unit| {
387            if let Some((symbol, name)) = function(unit) {
388                if let Some(symbol) = symbol {
389                    write!(writer, "{}  {:4} ", indent, symbol)?;
390                } else {
391                    write!(writer, "{}       ", indent)?;
392                }
393                if let Some(name) = name {
394                    writeln!(writer, "{}", name)?;
395                } else {
396                    writeln!(writer, "-")?;
397                }
398            }
399            Ok(())
400        })
401    }
402
403    fn translate_area(unit: AreaKind) -> Option<(Option<String>, Option<String>)> {
404        match unit {
405            Square(Metre(_)) => Self::translate_append(unit, "square-length"),
406            Square(Inch) => None,
407            Square(Foot) => None,
408            Square(Yard) => None,
409            Square(Mile) => None,
410            _ => Self::translate_generic(unit),
411        }
412    }
413
414    fn translate_volume(unit: VolumeKind) -> Option<(Option<String>, Option<String>)> {
415        match unit {
416            Cubic(Metre(_)) => Self::translate_append(unit, "cubic-length"),
417            Cubic(Inch) => None,
418            Cubic(Foot) => None,
419            Cubic(Yard) => None,
420            Cubic(Mile) => None,
421            _ => Self::translate_generic(unit),
422        }
423    }
424
425    fn translate_speed(unit: SpeedKind) -> Option<(Option<String>, Option<String>)> {
426        match unit {
427            Ratio(Metre(_), Second(_)) => Self::translate_append(unit, "length/time"),
428            Ratio(_, _) => None,
429            _ => Self::translate_generic(unit),
430        }
431    }
432
433    fn translate_append<U: UnitInfo>(unit: U, append: &str) -> Option<(Option<String>, Option<String>)> {
434        if let Some((symbol, unit)) = Self::translate_generic(unit) {
435            let unit = unit.map(|x| format!("{} (and other {} units)", x, append));
436            Some((symbol, unit))
437        } else {
438            None
439        }
440    }
441
442    fn translate_generic<U: UnitInfo>(unit: U) -> Option<(Option<String>, Option<String>)> {
443        let symbol = unit.get_symbol();
444        let name = unit.get_name();
445        if symbol.is_some() || name.is_some() {
446            Some((symbol, name))
447        } else {
448            None
449        }
450    }
451
452    // +---------------------- indent (2 chars)
453    // | +-------------------- input (5 chars)
454    // | |    +--------------- keyword (8 chars)
455    // | |    |       +------- output (5 chars)
456    // | |    |       |    +-- description
457    // v-v----v-------v----v--
458    //   N N  swap    N N  Swap two values on the stack
459
460    pub fn show_help(&self, writer: &mut W, interact: bool) -> MyResult<()> {
461        let indent = if interact { "  " } else { "" };
462        for description in self.descriptions.iter() {
463            match description {
464                Description::Separator(description) => {
465                    writeln!(writer, "{}{}:", indent, description)?;
466                }
467                Description::Operation((keyword, input, output, description)) => {
468                    writeln!(writer, "{}  {:>3}  {:7} {:3}  {}", indent, input, keyword, output, description)?;
469                }
470                Description::Directive((keyword, description)) => {
471                    writeln!(writer, "{}  {:>3}  {:12} {}", indent, "", keyword, description)?;
472                }
473            }
474        }
475        self.show_definitions(writer, interact)?;
476        Ok(())
477    }
478
479    pub fn show_definitions(&self, writer: &mut W, interact: bool) -> MyResult<()> {
480        let indent = if interact { "  " } else { "" };
481        if !self.definitions.is_empty() {
482            writeln!(writer, "{}Defined functions:", indent)?;
483            for (keyword, (_, input, output, description)) in self.definitions.iter() {
484                if let Count::N(0) = output {
485                    writeln!(writer, "{}  {:>3}  {:12} Function \"{}\"", indent, input, keyword, description)?;
486                } else {
487                    writeln!(writer, "{}  {:>3}  {:7} {:3}  Function \"{}\"", indent, input, keyword, output, description)?;
488                }
489            }
490        }
491        Ok(())
492    }
493}
494
495#[cfg(test)]
496pub mod tests {
497    use crate::calc::value::{Value, ValueRef};
498    use crate::core::count::Count;
499    use crate::core::interface::{Completion, Directive, Interface, Operation};
500    use crate::core::stack::ValueStack;
501    use crate::engine::Engine;
502    use crate::error::MyResult;
503    use crate::util::text::tests::BufferWriter;
504    use pretty_assertions::assert_eq;
505    use std::cell::RefCell;
506    use std::collections::HashMap;
507    use std::rc::Rc;
508
509    #[test]
510    fn test_completes_commands() {
511        let expected_commands = vec![
512            "eight",
513            "five",
514            "four",
515            "nine",
516            "one",
517            "seven",
518            "six",
519            "ten",
520            "three",
521            "two",
522        ];
523        let expected_completions = HashMap::from([
524            (String::from("five"), Completion::Filename),
525            (String::from("six"), Completion::Filename),
526            (String::from("seven"), Completion::Filename),
527            (String::from("eight"), Completion::Keyword),
528        ]);
529        let interface = Interface::<BufferWriter>::new()
530            .with_operation(vec!["one", "two"], Operation::ValueNone(dummy_nullary), "")
531            .with_operation(vec!["three"], Operation::ValueNone(dummy_nullary), "")
532            .with_operation(vec!["four"], Operation::ValueNone(dummy_nullary), "")
533            .with_directive(vec!["five", "six"], Directive::EngineAll(dummy_directive, Completion::Filename), "")
534            .with_directive(vec!["seven"], Directive::EngineAll(dummy_directive, Completion::Filename), "")
535            .with_directive(vec!["eight"], Directive::EngineAll(dummy_directive, Completion::Keyword), "")
536            .with_definition("nine", vec![], Count::N(0), Count::N(0), String::from(""))
537            .with_definition("ten", vec![], Count::N(0), Count::N(0), String::from(""));
538        assert_eq!(interface.get_commands(), expected_commands);
539        assert_eq!(interface.get_completions(), expected_completions);
540    }
541
542    pub fn dummy_nullary() -> MyResult<ValueRef> {
543        Ok(Rc::new(RefCell::new(Value::new(None))))
544    }
545
546    pub fn dummy_unary(_: ValueRef) -> MyResult<ValueRef> {
547        Ok(Rc::new(RefCell::new(Value::new(None))))
548    }
549
550    pub fn dummy_binary(_: ValueRef, _: ValueRef) -> MyResult<ValueRef> {
551        Ok(Rc::new(RefCell::new(Value::new(None))))
552    }
553
554    pub fn dummy_series(_: Vec<ValueRef>) -> MyResult<ValueRef> {
555        Ok(Rc::new(RefCell::new(Value::new(None))))
556    }
557
558    pub fn dummy_binary_series(_: ValueRef, _: ValueRef) -> MyResult<Vec<ValueRef>> {
559        Ok(Vec::new())
560    }
561
562    pub fn dummy_series_series(_: Vec<ValueRef>) -> MyResult<Vec<ValueRef>> {
563        Ok(Vec::new())
564    }
565
566    pub fn dummy_directive(
567        _engine: &mut Engine<BufferWriter>,
568        _writer: &mut BufferWriter,
569        _stack: &mut ValueStack,
570        _tokens: Vec<String>,
571    ) -> MyResult<bool> {
572        Ok(false)
573    }
574
575    #[test]
576    fn test_shows_measurement_units() {
577        let expected = "\
578Prefix units:
579  Q    quetta       10^30
580  R    ronna        10^27
581  Y    yotta        10^24
582  Z    zetta        10^21
583  E    exa          10^18
584  P    peta         10^15
585  T    tera         10^12
586  G    giga         10^9
587  M    mega         10^6
588  k    kilo         10^3
589  h    hecto        10^2
590  da   deca         10
591  -    -            1
592  d    deci         10^-1
593  c    centi        10^-2
594  m    milli        10^-3
595  u    micro        10^-6
596  n    nano         10^-9
597  p    pico         10^-12
598  f    femto        10^-15
599  a    atto         10^-18
600  z    zepto        10^-21
601  y    yocto        10^-24
602  r    ronto        10^-27
603  q    quecto       10^-30
604Time units:
605  s    second
606       minute
607       hour
608       day
609       week
610       month
611       year
612Length units:
613  m    metre
614       inch
615       foot
616       yard
617       mile
618Area units:
619  m^2  square-metre (and other square-length units)
620       hectare
621       acre
622Volume units:
623  m^3  cubic-metre (and other cubic-length units)
624  l    litre
625       tsp
626       tbsp
627       floz
628       pint
629       quart
630       gallon
631       barrel
632       ustsp
633       ustbsp
634       usfloz
635       uscup
636       uspint
637       usquart
638       usgallon
639       usbarrel
640Speed units:
641  m/s  metre/second (and other length/time units)
642       mach
643       light
644Mass units:
645  g    gram
646       ounce
647       pound
648       stone
649       ton
650Temperature units:
651  K    kelvin
652  C    celsius
653  F    fahrenheit
654  R    rankine
655Data units:
656  B    byte
657       bit
658";
659        let interface = Interface::build();
660        let mut writer = BufferWriter::new();
661        assert!(interface.show_units(&mut writer, false).is_ok());
662        assert_eq!(writer.buffer, expected);
663    }
664
665    #[test]
666    fn test_shows_help_text() {
667        let expected = "\
668Arithmetic operations:
669  N N  add,+   N    Add two values
670  N N  sub,-   N    Subtract two values
671  N N  mul,*   N    Multiply two values
672  N N  div,/   N    Divide two values
673  N N  mod,%   N    Modulo two values
674    N  neg     N    Find the negative
675    N  inv     N    Find the inverse
676  N N  pow     N    Raise to the power
677    N  sqrt    N    Find the square root
678    *  sum     N    Sum all values
679    *  prod    N    Multiply all values
680Sequence operations:
681  N N  seq     *    Generate integer sequence (start to stop)
682    3  step    *    Generate integer sequence (start with step to stop)
683    *  sort    *    Sort stack or sequence
684    *  rev     *    Reverse stack or sequence
685    *  flat    *    Flatten entire stack
686Bitwise operations:
687  N N  and     N    Bitwise AND two values
688  N N  or      N    Bitwise OR two values
689  N N  xor     N    Bitwise XOR two values
690  N N  shl     N    Shift left (multiply by power of 2)
691  N N  shr     N    Shift right (divide by power of 2)
692Time operations:
693       now     N    Get the current time (in UTC)
694    N  plain   N    Format as a plain value
695    N  delta   N    Format as a delta value (duration)
696    N  time    N    Format as a time value (in UTC)
697Formatting commands:
698       dec          Format as decimal values
699       hex          Format as hexadecimal values
700       sep          Include comma separators
701       nosep        Include no separators
702    N  dp           Use fixed decimal places
703       nodp         Use free decimal places
704Stack commands:
705       show         Show all values on the stack
706    *  clear        Remove all values from the stack
707    N  pop          Remove a value from the stack
708    N  dup     N N  Duplicate a value on the stack
709  N N  swap    N N  Swap two values on the stack
710    N  cut          Cut a value to the internal clipboard
711    N  copy    N    Copy a value to the internal clipboard
712       paste   N    Paste a value from the internal clipboard
713History commands:
714       u(ndo)       Undo the last operation
715       r(edo)       Redo the next operation
716       hist         Show all undo/redo history
717General directives:
718       import...    Import file e.g. \"import file.txt\"
719       export...    Export file e.g. \"export file.txt\"
720       set,=...     Set variable, e.g. \"set x\"
721       define...    Define function e.g. \"define cube 3 pow\"
722       apply...     Apply to stack or sequence, e.g. \"apply 3 pow\"
723General commands:
724       units        Show unit names and symbols
725       help         Show this help text
726";
727        let interface = Interface::build();
728        let mut writer = BufferWriter::new();
729        assert!(interface.show_help(&mut writer, false).is_ok());
730        assert_eq!(writer.buffer, expected);
731    }
732
733    #[test]
734    fn test_shows_definitions_with_null() {
735        let expected = "\
736Defined functions:
737       answer  N    Function \"6 7 mul\"
738    N  cube    N    Function \"3 pow\"
739  N N  very-long-a  Function \"\"
740  N N  very-long-bb Function \"\"
741  N N  very-long-ccc Function \"\"
742  N N  very-long-dddd Function \"\"
743  N N  very-long-eeeee Function \"\"
744";
745        let mut interface = Interface::build();
746        let mut writer = BufferWriter::new();
747        interface.insert_definition("answer", vec![], Count::N(0), Count::N(1), String::from("6 7 mul"));
748        interface.insert_definition("cube", vec![], Count::N(1), Count::N(1), String::from("3 pow"));
749        interface.insert_definition("very-long-a", vec![], Count::N(2), Count::N(0), String::from(""));
750        interface.insert_definition("very-long-bb", vec![], Count::N(2), Count::N(0), String::from(""));
751        interface.insert_definition("very-long-ccc", vec![], Count::N(2), Count::N(0), String::from(""));
752        interface.insert_definition("very-long-dddd", vec![], Count::N(2), Count::N(0), String::from(""));
753        interface.insert_definition("very-long-eeeee", vec![], Count::N(2), Count::N(0), String::from(""));
754        assert!(interface.show_definitions(&mut writer, false).is_ok());
755        assert_eq!(writer.buffer, expected);
756    }
757
758    #[test]
759    fn test_shows_definitions_with_value() {
760        let expected = "\
761Defined functions:
762       answer  N    Function \"6 7 mul\"
763    N  cube    N    Function \"3 pow\"
764  N N  long-a  N N  Function \"\"
765  N N  long-bb N N  Function \"\"
766  N N  long-ccc N N  Function \"\"
767  N N  long-dddd N N  Function \"\"
768  N N  long-eeeee N N  Function \"\"
769";
770        let mut interface = Interface::build();
771        let mut writer = BufferWriter::new();
772        interface.insert_definition("answer", vec![], Count::N(0), Count::N(1), String::from("6 7 mul"));
773        interface.insert_definition("cube", vec![], Count::N(1), Count::N(1), String::from("3 pow"));
774        interface.insert_definition("long-a", vec![], Count::N(2), Count::N(2), String::from(""));
775        interface.insert_definition("long-bb", vec![], Count::N(2), Count::N(2), String::from(""));
776        interface.insert_definition("long-ccc", vec![], Count::N(2), Count::N(2), String::from(""));
777        interface.insert_definition("long-dddd", vec![], Count::N(2), Count::N(2), String::from(""));
778        interface.insert_definition("long-eeeee", vec![], Count::N(2), Count::N(2), String::from(""));
779        assert!(interface.show_definitions(&mut writer, false).is_ok());
780        assert_eq!(writer.buffer, expected);
781    }
782}