reactive_graph_client/client/repl/
mod.rs

1use std::sync::Arc;
2
3use clap::Parser;
4use colored::*;
5use rustyline::Cmd;
6use rustyline::Editor;
7use rustyline::Event;
8use rustyline::KeyCode;
9use rustyline::KeyEvent;
10use rustyline::Modifiers;
11use rustyline::error::ReadlineError;
12use rustyline::history::DefaultHistory;
13use shellwords::split;
14
15use crate::client::handler::handle_command;
16use crate::client::repl::args::ReplArgs;
17use crate::client::repl::chars::*;
18use crate::client::repl::repl_helper::ReplHelper;
19use crate::client::repl::return_state::ReturnState;
20use reactive_graph_client::ReactiveGraphClient;
21
22pub(crate) mod args;
23pub(crate) mod chars;
24pub(crate) mod hint;
25pub(crate) mod repl_helper;
26pub(crate) mod return_state;
27
28pub(crate) async fn repl(client: &Arc<ReactiveGraphClient>) -> Result<(), i32> {
29    let mut rl = Editor::<ReplHelper, DefaultHistory>::new().map_err(|_| 255)?;
30    rl.set_helper(Some(ReplHelper::new()));
31    rl.bind_sequence(Event::KeySeq(vec![KeyEvent(KeyCode::Tab, Modifiers::NONE)]), Cmd::CompleteHint);
32
33    if rl.load_history("history.txt").is_err() {
34        println!("No previous history.");
35    }
36
37    let mut return_state = ReturnState::Neutral;
38    let mut break_state = false;
39    loop {
40        let prompt = format!("{} {} {} ", CHAR_PROMPT, client.remote().base_url().cyan().bold(), return_state);
41        let readline = rl.readline(prompt.as_str());
42        match readline {
43            Ok(line) => {
44                match line.as_str() {
45                    "exit" | "quit" => break,
46                    _ => match split(line.as_str())
47                        .map(|mut args| {
48                            args.insert(0, String::from(" "));
49                            args
50                        })
51                        .map(ReplArgs::try_parse_from)
52                    {
53                        Ok(Ok(cli_args)) => {
54                            let command = cli_args.commands;
55                            match handle_command(client, command).await {
56                                Ok(response) => {
57                                    println!("{response}");
58                                    // input was executed (successful or not)
59                                    return_state = ReturnState::Success;
60                                    break_state = false;
61                                }
62                                Err(e) => {
63                                    eprintln!("{}: {}", "Command failed with error".red(), e);
64                                    // input errored
65                                    return_state = ReturnState::Error;
66                                }
67                            }
68                        }
69                        Ok(Err(e)) => {
70                            eprintln!("{e}");
71                            return_state = ReturnState::Error;
72                        }
73                        Err(r) => {
74                            eprintln!("{}: {}", "Mismatched Quotes".red(), r);
75                            return_state = ReturnState::Error;
76                        }
77                    },
78                }
79                let _ = rl.add_history_entry(line.as_str());
80            }
81            // CTRL-C
82            Err(ReadlineError::Interrupted) => {
83                if break_state {
84                    break;
85                }
86                break_state = true;
87                println!("Press CTRL-C again to exit");
88            }
89            // CTRL-D
90            Err(ReadlineError::Eof) => break,
91            Err(e) => {
92                eprintln!("Error: {e:?}");
93                break;
94            }
95        }
96    }
97    let _ = rl.save_history("history.txt");
98    Ok(())
99}
100
101pub fn longest_common_prefix(s: &Vec<&String>) -> String {
102    let mut result = "".to_string();
103    let mut count = 0;
104    let mut found = false;
105    if s.is_empty() || s[0].is_empty() {
106        return result;
107    }
108    loop {
109        result.push_str(&s[0][count..count + 1]);
110        for item in s {
111            if item.len() < count + 1 || item[0..count + 1] != result {
112                found = true;
113                break;
114            }
115        }
116        match found {
117            true => break result[0..count].to_string(),
118            false => {
119                if count + 1 == s[0].len() {
120                    break result;
121                }
122            }
123        }
124        count += 1;
125    }
126}