1mod commands;
2mod state;
3
4pub use commands::parse_int;
5
6use crate::{
7 error::{Error, Result},
8 FuelClient,
9};
10use rustyline::{CompletionType, Config, EditMode, Editor};
11use state::{DebuggerHelper, State};
12use std::path::PathBuf;
13
14pub async fn start_cli(api_url: &str) -> Result<()> {
16 let mut cli = Cli::new()?;
17 cli.run(api_url).await
18}
19
20pub struct Cli {
21 editor: Editor<DebuggerHelper, rustyline::history::FileHistory>,
22 history_path: PathBuf,
23}
24
25impl Drop for Cli {
26 fn drop(&mut self) {
27 let _ = self.editor.save_history(&self.history_path);
29 }
30}
31
32impl Cli {
33 pub fn new() -> Result<Self> {
34 let config = Config::builder()
36 .auto_add_history(true)
37 .history_ignore_space(true)
38 .completion_type(CompletionType::Circular)
39 .edit_mode(EditMode::Vi)
40 .max_history_size(100)?
41 .build();
42
43 let mut editor = Editor::with_config(config)?;
44 let helper = DebuggerHelper::new();
45 editor.set_helper(Some(helper));
46
47 let history_path = get_history_file_path()?;
49 let _ = editor.load_history(&history_path);
50
51 Ok(Self {
52 editor,
53 history_path,
54 })
55 }
56
57 pub async fn run(&mut self, api_url: &str) -> Result<()> {
58 let client = FuelClient::new(api_url).map_err(|e| Error::FuelClientError(e.to_string()))?;
59 let mut state = State::new(client);
60
61 state.session_id = state
63 .client
64 .start_session()
65 .await
66 .map_err(|e| Error::FuelClientError(e.to_string()))?;
67
68 println!("Welcome to the Sway Debugger! Type \"help\" for a list of commands.");
69
70 loop {
72 let readline = self.editor.readline(">> ");
73 match readline {
74 Ok(line) => {
75 let args: Vec<String> = line.split_whitespace().map(String::from).collect();
76
77 if args.is_empty() {
78 continue;
79 }
80
81 if let Some(helper) = self.editor.helper() {
82 match args[0].as_str() {
83 cmd if helper.commands.is_help_command(cmd) => {
84 if let Err(e) = commands::cmd_help(helper, &args).await {
85 println!("Error: {}", e);
86 }
87 }
88 cmd if helper.commands.is_tx_command(cmd) => {
89 if let Err(e) = commands::cmd_start_tx(&mut state, args).await {
90 println!("Error: {}", e);
91 }
92 }
93 cmd if helper.commands.is_register_command(cmd) => {
94 if let Err(e) = commands::cmd_registers(&mut state, args).await {
95 println!("Error: {}", e);
96 }
97 }
98 cmd if helper.commands.is_breakpoint_command(cmd) => {
99 if let Err(e) = commands::cmd_breakpoint(&mut state, args).await {
100 println!("Error: {}", e);
101 }
102 }
103 cmd if helper.commands.is_memory_command(cmd) => {
104 if let Err(e) = commands::cmd_memory(&mut state, args).await {
105 println!("Error: {}", e);
106 }
107 }
108 cmd if helper.commands.is_quit_command(cmd) => {
109 break Ok(());
110 }
111 cmd if helper.commands.is_reset_command(cmd) => {
112 if let Err(e) = commands::cmd_reset(&mut state, args).await {
113 println!("Error: {}", e);
114 }
115 }
116 cmd if helper.commands.is_continue_command(cmd) => {
117 if let Err(e) = commands::cmd_continue(&mut state, args).await {
118 println!("Error: {}", e);
119 }
120 }
121 cmd if helper.commands.is_step_command(cmd) => {
122 if let Err(e) = commands::cmd_step(&mut state, args).await {
123 println!("Error: {}", e);
124 }
125 }
126 unknown_cmd => {
127 if let Some(suggestion) = helper.commands.find_closest(unknown_cmd)
128 {
129 println!(
130 "Unknown command: '{}'. Did you mean '{}'?",
131 unknown_cmd, suggestion.name
132 );
133 } else {
134 println!("Unknown command: '{}'", unknown_cmd);
135 }
136 }
137 }
138 }
139 }
140 Err(rustyline::error::ReadlineError::Interrupted) => {
141 println!("CTRL-C");
142 break Ok(());
143 }
144 Err(rustyline::error::ReadlineError::Eof) => {
145 println!("CTRL-D");
146 break Ok(());
147 }
148 Err(err) => {
149 println!("Error: {}", err);
150 break Ok(());
151 }
152 }
153 }
154 }
155}
156
157fn get_history_file_path() -> Result<PathBuf> {
158 let home = dirs::home_dir().ok_or_else(|| {
159 Error::IoError(std::io::Error::new(
160 std::io::ErrorKind::NotFound,
161 "Could not find home directory",
162 ))
163 })?;
164 let debug_dir = home.join(".forc").join(".debug");
165 std::fs::create_dir_all(&debug_dir).map_err(Error::IoError)?;
166 Ok(debug_dir.join("history"))
167}