1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use datafusion::sql::parser::{DFParser, Statement};
use rustyline::completion::Completer;
use rustyline::completion::FilenameCompleter;
use rustyline::completion::Pair;
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use rustyline::validate::ValidationContext;
use rustyline::validate::ValidationResult;
use rustyline::validate::Validator;
use rustyline::Context;
use rustyline::Helper;
use rustyline::Result;
#[derive(Default)]
pub struct CliHelper {
completer: FilenameCompleter,
}
impl Highlighter for CliHelper {}
impl Hinter for CliHelper {
type Hint = String;
}
fn is_open_quote_for_location(line: &str, pos: usize) -> bool {
let mut sql = line[..pos].to_string();
sql.push('\'');
if let Ok(stmts) = DFParser::parse_sql(&sql) {
if let Some(Statement::CreateExternalTable(_)) = stmts.back() {
return true;
}
}
false
}
impl Completer for CliHelper {
type Candidate = Pair;
fn complete(
&self,
line: &str,
pos: usize,
ctx: &Context<'_>,
) -> std::result::Result<(usize, Vec<Pair>), ReadlineError> {
if is_open_quote_for_location(line, pos) {
self.completer.complete(line, pos, ctx)
} else {
Ok((0, Vec::with_capacity(0)))
}
}
}
impl Validator for CliHelper {
fn validate(&self, ctx: &mut ValidationContext<'_>) -> Result<ValidationResult> {
let input = ctx.input().trim_end();
if let Some(sql) = input.strip_suffix(';') {
match DFParser::parse_sql(sql) {
Ok(statements) if statements.is_empty() => Ok(ValidationResult::Invalid(
Some(" 🤔 You entered an empty statement".to_string()),
)),
Ok(statements) if statements.len() > 1 => Ok(ValidationResult::Invalid(
Some(" 🤔 You entered more than one statement".to_string()),
)),
Ok(_statements) => Ok(ValidationResult::Valid(None)),
Err(err) => Ok(ValidationResult::Invalid(Some(format!(
" 🤔 Invalid statement: {}",
err
)))),
}
} else if input.starts_with('\\') {
Ok(ValidationResult::Valid(None))
} else {
Ok(ValidationResult::Incomplete)
}
}
}
impl Helper for CliHelper {}