Skip to content

Commit

Permalink
Implemented ctrl-C exit, clearsreen (ctrl-l) and fix spaces not printing
Browse files Browse the repository at this point in the history
  • Loading branch information
deven96 committed Oct 3, 2024
1 parent 65840a1 commit 3b71a19
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 91 deletions.
2 changes: 1 addition & 1 deletion ahnlich/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ path = "src/lib.rs"


[dependencies]
crossterm = "0.28.1"
crossterm = { version = "0.28.1", feature = ["bracketed-paste"]}
clap.workspace = true
dsl = { path = "../dsl", version = "*" }
thiserror.workspace = true
Expand Down
8 changes: 8 additions & 0 deletions ahnlich/cli/src/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ impl AgentPool {
}
}

/// Returns the commands for the agent pool in question
pub fn commands(&self) -> &[&str] {
match self {
AgentPool::AI(_) => dsl::ai::COMMANDS,
AgentPool::DB(_) => dsl::db::COMMANDS,
}
}

/// Checks if the connection to to a host and post is alive, also checks the cli is connected
/// to the right server( ahnlich ai or db)
pub async fn is_valid_connection(&self) -> Result<bool, String> {
Expand Down
149 changes: 97 additions & 52 deletions ahnlich/cli/src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ use crossterm::{
event::{self, Event, KeyCode, KeyEvent},
queue,
style::{Color, Print, SetForegroundColor, Stylize},
terminal::{disable_raw_mode, enable_raw_mode},
terminal::{self, disable_raw_mode, enable_raw_mode},
ExecutableCommand,
};
use std::io::{self, stdout, Stdout, Write};

use crate::connect::AgentPool;

const RESERVED_WORDS: [&str; 3] = ["ping", "infoserver", "createpredindex"];

#[derive(Debug)]
enum SpecialEntry {
Enter,
Expand All @@ -20,6 +18,8 @@ enum SpecialEntry {
Left,
Right,
Del,
Exit,
ClrScr,
}

#[derive(Debug)]
Expand All @@ -29,6 +29,12 @@ enum Entry {
None,
}

#[derive(Debug)]
enum LineResult {
Command(String),
Exit,
}

pub struct Term {
client_pool: AgentPool,
}
Expand All @@ -39,29 +45,40 @@ impl Term {
}

fn read_char(&self) -> io::Result<Entry> {
if let Event::Key(KeyEvent { code, .. }) = event::read()? {
Ok(match code {
KeyCode::Enter => Entry::Special(SpecialEntry::Enter),
KeyCode::Char(c) => Entry::Char(c),
KeyCode::Left => Entry::Special(SpecialEntry::Left),
KeyCode::Up => Entry::Special(SpecialEntry::Up),
KeyCode::Down => Entry::Special(SpecialEntry::Down),
KeyCode::Right => Entry::Special(SpecialEntry::Right),
KeyCode::Backspace => Entry::Special(SpecialEntry::Del),
_ => Entry::None,
})
} else {
Ok(Entry::None)
match event::read()? {
Event::Key(KeyEvent {
code, modifiers, ..
}) => {
if code == KeyCode::Char('c') && modifiers == event::KeyModifiers::CONTROL {
return Ok(Entry::Special(SpecialEntry::Exit));
}
if code == KeyCode::Char('l') && modifiers == event::KeyModifiers::CONTROL {
return Ok(Entry::Special(SpecialEntry::ClrScr));
}
Ok(match code {
KeyCode::Enter => Entry::Special(SpecialEntry::Enter),
KeyCode::Char(c) => Entry::Char(c),
KeyCode::Left => Entry::Special(SpecialEntry::Left),
KeyCode::Up => Entry::Special(SpecialEntry::Up),
KeyCode::Down => Entry::Special(SpecialEntry::Down),
KeyCode::Right => Entry::Special(SpecialEntry::Right),
KeyCode::Backspace => Entry::Special(SpecialEntry::Del),
_ => Entry::None,
})
}
_ => Ok(Entry::None),
}
}
pub fn welcome_message(&self) -> io::Result<()> {
let mut stdout = stdout();
stdout.execute(SetForegroundColor(Color::White))?;
stdout.execute(Print(format!(
"Welcome To Ahnlich {}\n\n",
self.client_pool
)))?;
stdout.execute(SetForegroundColor(Color::White))?;
queue!(
stdout,
terminal::Clear(terminal::ClearType::All),
cursor::MoveTo(0, 0),
SetForegroundColor(Color::White),
Print(format!("Welcome To Ahnlich {}\n\n", self.client_pool)),
SetForegroundColor(Color::White),
)?;
stdout.flush()?;
Ok(())
}
Expand All @@ -76,13 +93,26 @@ impl Term {
}

pub(crate) fn format_output(&self, query: &str) -> String {
let output = String::from_iter(query.split(' ').map(|ex| {
if RESERVED_WORDS.contains(&(ex.to_lowercase().as_str())) {
format!("{}", ex.magenta())
} else {
format!("{}", ex.white())
}
}));
let matches = |c| c == ';' || c == ' ';
let output = query
.split_inclusive(matches)
.map(|ex| {
// Trim the trailing space or semicolon from the command part
let trimmed_ex = ex.trim_end_matches(matches);

if self
.client_pool
.commands()
.contains(&(trimmed_ex.to_lowercase().as_str()))
{
// Add back the space or semicolon at the end (if present)
format!("{}{}", trimmed_ex.magenta(), &ex[trimmed_ex.len()..])
} else {
format!("{}{}", trimmed_ex.white(), &ex[trimmed_ex.len()..])
}
})
.collect::<String>();

output
}

Expand Down Expand Up @@ -131,7 +161,7 @@ impl Term {
Ok(())
}

fn read_line(&self, stdout: &mut Stdout) -> io::Result<String> {
fn read_line(&self, stdout: &mut Stdout) -> io::Result<LineResult> {
let (start_pos_col, _) = cursor::position()?;
let mut output = String::new();

Expand Down Expand Up @@ -174,13 +204,23 @@ impl Term {
stdout.execute(cursor::MoveToColumn(current_pos_col - 1))?;
}
}
SpecialEntry::ClrScr => {
queue!(
stdout,
cursor::MoveTo(0, 0),
terminal::Clear(terminal::ClearType::All),
)?;
self.ahnlich_prompt(stdout)?;
self.move_to_pos_and_print(stdout, &output, start_pos_col)?;
}
SpecialEntry::Exit => return Ok(LineResult::Exit),
},
Entry::None => {
continue;
}
}
}
Ok(output)
Ok(LineResult::Command(output))
}

pub async fn run(&self) -> io::Result<()> {
Expand All @@ -192,34 +232,39 @@ impl Term {
loop {
self.ahnlich_prompt(&mut stdout)?;
let input = self.read_line(&mut stdout)?;
match input.as_str() {
"quit" | "exit" | "exit()" => break,
command => {
let response = self.client_pool.parse_queries(command).await;

match response {
Ok(success) => {
disable_raw_mode()?;
for msg in success {
match input {
LineResult::Exit => {
break;
}
LineResult::Command(input) => match input.as_str() {
"quit" | "exit" | "exit()" => break,
command => {
let response = self.client_pool.parse_queries(command).await;

match response {
Ok(success) => {
disable_raw_mode()?;
for msg in success {
queue!(
stdout,
Print(format!("{}\n", msg)),
cursor::MoveToColumn(0)
)?;
}
stdout.flush()?;
enable_raw_mode()?
}
Err(err) => {
queue!(
stdout,
Print(format!("{}\n", msg)),
Print(format!("{}\n", err.red())),
cursor::MoveToColumn(0)
)?;
stdout.flush()?;
}
stdout.flush()?;
enable_raw_mode()?
}
Err(err) => {
queue!(
stdout,
Print(format!("{}\n", err.red())),
cursor::MoveToColumn(0)
)?;
stdout.flush()?;
}
}
}
},
};
}
disable_raw_mode()?;
Expand Down
34 changes: 18 additions & 16 deletions ahnlich/dsl/src/ai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,24 @@ fn parse_to_ai_model(input: &str) -> Result<AIModel, DslError> {

// Parse raw strings separated by ; into a Vec<AIQuery>. Examples include but are not restricted
// to
//
// PING
// LISTCLIENTS
// LISTSTORES
// INFOSERVER
// PURGESTORES
// DROPSTORE store_name IF EXISTS
// CREATEPREDINDEX (key_1, key_2) in store_name
// DROPPREDINDEX IF EXISTS (key1, key2) in store_name
// CREATENONLINEARALGORITHMINDEX (kdtree) in store_name
// DROPNONLINEARALGORITHMINDEX IF EXISTS (kdtree) in store_name
// DELKEY ([input 1 text], [input 2 text]) IN my_store
// GETPRED ((author = dickens) OR (country != Nigeria)) IN my_store
// GETSIMN 4 WITH [random text inserted here] USING cosinesimilarity IN my_store WHERE (author = dickens)
// CREATESTORE IF NOT EXISTS my_store QUERYMODEL dalle3 INDEXMODEL dalle3 PREDICATES (author, country) NONLINEARALGORITHMINDEX (kdtree)
// SET (([This is the life of Haks paragraphed], {name: Haks, category: dev}), ([This is the life of Deven paragraphed], {name: Deven, category: dev})) in store
pub const COMMANDS: &[&str] = &[
"ping",
"listclients",
"liststores",
"infoserver",
"purgestores",
"dropstore", // store_name if exists can be handled dynamically
"createpredindex", // (key_1, key_2) in store_name
"droppredindex", // if exists (key1, key2) in store_name
"createnonlinearalgorithmindex", // (kdtree) in store_name
"dropnonlinearalgorithmindex", // if exists (kdtree) in store_name
"delkey", // ([input 1 text], [input 2 text]) in my_store
"getpred", // ((author = dickens) or (country != Nigeria)) in my_store
"getsimn", // 4 with [random text inserted here] using cosinesimilarity in my_store where (author = dickens)
"createstore", // if not exists my_store querymodel dalle3 indexmodel dalle3 predicates (author, country) nonlinearalgorithmindex (kdtree)
"set", // (([This is the life of Haks paragraphed], {name: Haks, category: dev}), ([This is the life of Deven paragraphed], {name: Deven, category: dev})) in store
];

pub fn parse_ai_query(input: &str) -> Result<Vec<AIQuery>, DslError> {
let pairs = QueryParser::parse(Rule::ai_query, input).map_err(Box::new)?;
let statements = pairs.into_iter().collect::<Vec<_>>();
Expand Down
34 changes: 18 additions & 16 deletions ahnlich/dsl/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,24 @@ use crate::{error::DslError, predicate::parse_predicate_expression};

// Parse raw strings separated by ; into a Vec<DBQuery>. Examples include but are not restricted
// to
//
// PING
// LISTCLIENTS
// LISTSTORES
// INFOSERVER
// DROPSTORE store_name IF EXISTS
// CREATEPREDINDEX (key_1, key_2) in store_name
// DROPPREDINDEX IF EXISTS (key1, key2) in store_name
// CREATENONLINEARALGORITHMINDEX (kdtree) in store_name
// DROPNONLINEARALGORITHMINDEX IF EXISTS (kdtree) in store_name
// GETKEY ([1.0, 2.0], [3.0, 4.0]) IN my_store
// DELKEY ([1.2, 3.0], [5.6, 7.8]) IN my_store
// GETPRED ((author = dickens) OR (country != Nigeria)) IN my_store
// GETSIMN 4 WITH [0.65, 2.78] USING cosinesimilarity IN my_store WHERE (author = dickens)
// CREATESTORE IF NOT EXISTS my_store DIMENSION 21 PREDICATES (author, country) NONLINEARALGORITHMINDEX (kdtree)
// SET (([1.0, 2.1, 3.2], {name: Haks, category: dev}), ([3.1, 4.8, 5.0], {name: Deven, category: dev})) in store
pub const COMMANDS: &[&str] = &[
"ping",
"listclients",
"liststores",
"infoserver",
"dropstore", // store_name if exists can be handled dynamically
"createpredindex", // (key_1, key_2) in store_name
"droppredindex", // if exists (key1, key2) in store_name
"createnonlinearalgorithmindex", // (kdtree) in store_name
"dropnonlinearalgorithmindex", // if exists (kdtree) in store_name
"getkey", // ([1.0, 2.0], [3.0, 4.0]) in my_store
"delkey", // ([1.2, 3.0], [5.6, 7.8]) in my_store
"getpred", // ((author = dickens) or (country != Nigeria)) in my_store
"getsimn", // 4 with [0.65, 2.78] using cosinesimilarity in my_store where (author = dickens)
"createstore", // if not exists my_store dimension 21 predicates (author, country) nonlinearalgorithmindex (kdtree)
"set", // (([1.0, 2.1, 3.2], {name: Haks, category: dev}), ([3.1, 4.8, 5.0], {name: Deven, category: dev})) in store
];

pub fn parse_db_query(input: &str) -> Result<Vec<DBQuery>, DslError> {
let pairs = QueryParser::parse(Rule::db_query, input).map_err(Box::new)?;
let statements = pairs.into_iter().collect::<Vec<_>>();
Expand Down
12 changes: 6 additions & 6 deletions ahnlich/dsl/src/syntax/syntax.pest
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ db_statement = _{
invalid_statement
}

ping = { whitespace* ~ ^"ping" ~ whitespace* }
info_server = { whitespace* ~ ^"infoserver" ~ whitespace* }
list_stores = { whitespace* ~ ^"liststores" ~ whitespace* }
list_clients = { whitespace* ~ ^"listclients" ~ whitespace* }
purge_stores = { whitespace* ~ ^"purgestores" ~ whitespace* }
drop_store = { whitespace* ~ ^"dropstore" ~ whitespace* ~ store_name ~ (if_exists | invalid_statement)? }
ping = { whitespace* ~ ^"ping" ~ whitespace* ~ !(ASCII_ALPHANUMERIC) }
info_server = { whitespace* ~ ^"infoserver" ~ whitespace* ~ !(ASCII_ALPHANUMERIC)}
list_stores = { whitespace* ~ ^"liststores" ~ whitespace* ~ !(ASCII_ALPHANUMERIC)}
list_clients = { whitespace* ~ ^"listclients" ~ whitespace* ~ !(ASCII_ALPHANUMERIC)}
purge_stores = { whitespace* ~ ^"purgestores" ~ whitespace* ~ !(ASCII_ALPHANUMERIC)}
drop_store = { whitespace* ~ ^"dropstore" ~ whitespace* ~ store_name ~ (if_exists)? ~ !(ASCII_ALPHANUMERIC)}
create_pred_index = { whitespace* ~ ^"createpredindex" ~ whitespace* ~ "(" ~ index_names ~ ")" ~ in_ignored ~ store_name }
create_non_linear_algorithm_index = { whitespace* ~ ^"createnonlinearalgorithmindex" ~ whitespace* ~ "(" ~ non_linear_algorithms ~ ")" ~ in_ignored ~ store_name}
drop_pred_index = { whitespace* ~ ^"droppredindex" ~ whitespace* ~ (if_exists)? ~ "(" ~ index_names ~ ")" ~ in_ignored ~ store_name }
Expand Down

0 comments on commit 3b71a19

Please sign in to comment.