nickel_lang_core/repl/
command.rs1use super::*;
3use std::fmt;
4
5#[derive(Copy, Clone, Eq, PartialEq, Debug)]
7pub enum CommandType {
8 Load,
9 Typecheck,
10 Query,
11 Print,
12 Help,
13 Exit,
14}
15
16impl CommandType {
17 pub fn all() -> Vec<&'static str> {
18 vec!["load", "typecheck", "query", "print", "help", "exit"]
19 }
20}
21
22#[derive(Clone, Eq, PartialEq, Debug)]
25pub enum Command {
26 Load(OsString),
27 Typecheck(String),
28 Query(String),
29 Print(String),
30 Help(Option<String>),
31 Exit,
32}
33
34pub struct UnknownCommandError {}
35
36fn require_arg(cmd: CommandType, arg: &str, msg_opt: Option<&str>) -> Result<(), ReplErrorKind> {
38 if arg.trim().is_empty() {
39 Err(ReplErrorKind::MissingArg {
40 cmd,
41 msg_opt: msg_opt.map(String::from),
42 })
43 } else {
44 Ok(())
45 }
46}
47
48impl FromStr for CommandType {
49 type Err = UnknownCommandError;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 use CommandType::*;
53
54 match s {
55 "load" | "l" => Ok(Load),
56 "typecheck" | "tc" => Ok(Typecheck),
57 "query" | "q" => Ok(Query),
58 "print" | "p" => Ok(Print),
59 "help" | "?" | "h" => Ok(Help),
60 "exit" | "e" => Ok(Exit),
61 _ => Err(UnknownCommandError {}),
62 }
63 }
64}
65
66impl CommandType {
67 pub fn aliases(&self) -> Vec<String> {
69 use CommandType::*;
70
71 match self {
72 Load => vec![String::from("l")],
73 Typecheck => vec![String::from("tc")],
74 Query => vec![String::from("q")],
75 Print => vec![String::from("p")],
76 Help => vec![String::from("h"), String::from("?")],
77 Exit => vec![String::from("e")],
78 }
79 }
80}
81
82impl std::fmt::Display for CommandType {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 use CommandType::*;
85
86 match self {
87 Load => write!(f, "load"),
88 Typecheck => write!(f, "typecheck"),
89 Query => write!(f, "query"),
90 Print => write!(f, "print"),
91 Help => write!(f, "help"),
92 Exit => write!(f, "exit"),
93 }
94 }
95}
96
97impl FromStr for Command {
98 type Err = ReplErrorKind;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 let cmd_end = s.find(' ').unwrap_or(s.len());
102 let cmd_str: String = s.chars().take(cmd_end).collect();
103 let cmd: CommandType = cmd_str
104 .parse()
105 .map_err(|_| ReplErrorKind::UnknownCommand(cmd_str.clone()))?;
106 let arg: String = s
107 .chars()
108 .skip(cmd_end + 1)
109 .collect::<String>()
110 .trim()
111 .to_string();
112
113 match cmd {
114 CommandType::Load => {
115 require_arg(cmd, &arg, Some("Please provide a file to load"))?;
116 let arg = if arg.starts_with('"') && arg.ends_with('"') {
117 arg.chars().skip(1).take(arg.len() - 2).collect::<String>()
118 } else {
119 arg
120 };
121 println!("{arg}");
122 Ok(Command::Load(OsString::from(arg)))
123 }
124 CommandType::Typecheck => {
125 require_arg(cmd, &arg, None)?;
126 Ok(Command::Typecheck(arg))
127 }
128 CommandType::Query => {
129 require_arg(cmd, &arg, None)?;
130 Ok(Command::Query(arg))
131 }
132 CommandType::Print => {
133 require_arg(cmd, &arg, None)?;
134 Ok(Command::Print(arg))
135 }
136 CommandType::Exit => Ok(Command::Exit),
137 CommandType::Help => {
138 let arg_opt = if arg.trim().is_empty() {
139 None
140 } else {
141 Some(String::from(arg.trim()))
142 };
143
144 Ok(Command::Help(arg_opt))
145 }
146 }
147 }
148}
149
150impl Command {
151 pub fn typ(&self) -> CommandType {
152 use Command::*;
153
154 match self {
155 Load(..) => CommandType::Load,
156 Typecheck(..) => CommandType::Typecheck,
157 Query { .. } => CommandType::Query,
158 Print(..) => CommandType::Print,
159 Help(..) => CommandType::Help,
160 Exit => CommandType::Exit,
161 }
162 }
163}