1
0

Add JSON functions; Modify CLI prompt

This commit is contained in:
Jeff 2024-01-26 17:14:57 -05:00
parent f6a1e641c9
commit 3d21196768
8 changed files with 157 additions and 53 deletions

1
Cargo.lock generated
View File

@ -1022,6 +1022,7 @@ dependencies = [
"enum-iterator",
"env_logger",
"getrandom",
"humantime",
"libc",
"log",
"nu-ansi-term",

View File

@ -42,6 +42,7 @@ env_logger = "0.10"
reedline = { version = "0.28.0", features = ["clipboard", "sqlite"] }
crossterm = "0.27.0"
nu-ansi-term = "0.49.0"
humantime = "2.1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
env_logger = "0.10"

View File

@ -4,8 +4,8 @@ use enum_iterator::{all, Sequence};
use serde::{Deserialize, Serialize};
use crate::{
built_in_functions::string_functions, AbstractTree, BuiltInFunction, Format, Function, List,
Map, Result, SyntaxNode, Type, Value,
built_in_functions::{json::json_functions, string::string_functions, Callable},
AbstractTree, BuiltInFunction, Format, Function, List, Map, Result, SyntaxNode, Type, Value,
};
static ARGS: OnceLock<Value> = OnceLock::new();
@ -80,16 +80,18 @@ impl BuiltInValue {
Value::Map(fs_context)
}),
BuiltInValue::Json => JSON.get_or_init(|| {
let json_context = Map::new();
let mut json_context = BTreeMap::new();
json_context
.set(
BuiltInFunction::JsonParse.name().to_string(),
Value::Function(Function::BuiltIn(BuiltInFunction::JsonParse)),
)
.unwrap();
for json_function in json_functions() {
let key = json_function.name().to_string();
let value =
Value::Function(Function::BuiltIn(BuiltInFunction::Json(json_function)));
let r#type = value.r#type();
Value::Map(json_context)
json_context.insert(key, (value, r#type));
}
Value::Map(Map::with_variables(json_context))
}),
BuiltInValue::Length => &Value::Function(Function::BuiltIn(BuiltInFunction::Length)),
BuiltInValue::Output => &Value::Function(Function::BuiltIn(BuiltInFunction::Output)),

View File

@ -0,0 +1,64 @@
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use crate::{Error, Map, Result, Type, Value};
use super::Callable;
pub fn json_functions() -> impl Iterator<Item = Json> {
enum_iterator::all()
}
#[derive(Sequence, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum Json {
Create,
CreatePretty,
Parse,
}
impl Callable for Json {
fn name(&self) -> &'static str {
match self {
Json::Create => "create",
Json::CreatePretty => "create_pretty",
Json::Parse => "parse",
}
}
fn r#type(&self) -> Type {
match self {
Json::Create => Type::function(vec![Type::Any], Type::String),
Json::CreatePretty => Type::function(vec![Type::Any], Type::String),
Json::Parse => Type::function(vec![Type::String], Type::Any),
}
}
fn call(&self, arguments: &[Value], _source: &str, _outer_context: &Map) -> Result<Value> {
match self {
Json::Create => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap();
let json_string = serde_json::to_string(value)?;
Ok(Value::String(json_string))
}
Json::CreatePretty => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap();
let json_string = serde_json::to_string_pretty(value)?;
Ok(Value::String(json_string))
}
Json::Parse => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
let json_string = arguments.first().unwrap().as_string()?;
let value = serde_json::from_str(json_string)?;
Ok(value)
}
}
}
}

View File

@ -1,4 +1,5 @@
mod string;
pub mod json;
pub mod string;
use std::{
fmt::{self, Display, Formatter},
@ -10,13 +11,19 @@ use serde::{Deserialize, Serialize};
use crate::{Error, Format, Map, Result, Type, Value};
pub use string::{string_functions, StringFunction};
use self::{json::Json, string::StringFunction};
pub trait Callable {
fn name(&self) -> &'static str;
fn r#type(&self) -> Type;
fn call(&self, arguments: &[Value], source: &str, outer_context: &Map) -> Result<Value>;
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum BuiltInFunction {
AssertEqual,
FsRead,
JsonParse,
Json(Json),
Length,
Output,
RandomBoolean,
@ -26,12 +33,12 @@ pub enum BuiltInFunction {
String(StringFunction),
}
impl BuiltInFunction {
pub fn name(&self) -> &'static str {
impl Callable for BuiltInFunction {
fn name(&self) -> &'static str {
match self {
BuiltInFunction::AssertEqual => "assert_equal",
BuiltInFunction::FsRead => "read",
BuiltInFunction::JsonParse => "parse",
BuiltInFunction::Json(json_function) => json_function.name(),
BuiltInFunction::Length => "length",
BuiltInFunction::Output => "output",
BuiltInFunction::RandomBoolean => "boolean",
@ -42,11 +49,11 @@ impl BuiltInFunction {
}
}
pub fn r#type(&self) -> Type {
fn r#type(&self) -> Type {
match self {
BuiltInFunction::AssertEqual => Type::function(vec![Type::Any, Type::Any], Type::None),
BuiltInFunction::FsRead => Type::function(vec![Type::String], Type::String),
BuiltInFunction::JsonParse => Type::function(vec![Type::String], Type::Any),
BuiltInFunction::Json(json_function) => json_function.r#type(),
BuiltInFunction::Length => Type::function(vec![Type::Collection], Type::Integer),
BuiltInFunction::Output => Type::function(vec![Type::Any], Type::None),
BuiltInFunction::RandomBoolean => Type::function(vec![], Type::Boolean),
@ -57,7 +64,7 @@ impl BuiltInFunction {
}
}
pub fn call(&self, arguments: &[Value], _source: &str, _outer_context: &Map) -> Result<Value> {
fn call(&self, arguments: &[Value], _source: &str, _outer_context: &Map) -> Result<Value> {
match self {
BuiltInFunction::AssertEqual => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?;
@ -75,13 +82,8 @@ impl BuiltInFunction {
Ok(Value::string(file_content))
}
BuiltInFunction::JsonParse => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?;
let value = serde_json::from_str(string)?;
Ok(value)
BuiltInFunction::Json(json_function) => {
json_function.call(arguments, _source, _outer_context)
}
BuiltInFunction::Length => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;

View File

@ -1,10 +1,12 @@
use enum_iterator::{all, Sequence};
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use crate::{Error, List, Map, Result, Type, Value};
use super::Callable;
pub fn string_functions() -> impl Iterator<Item = StringFunction> {
all()
enum_iterator::all()
}
#[derive(Sequence, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
@ -41,8 +43,8 @@ pub enum StringFunction {
Truncate,
}
impl StringFunction {
pub fn name(&self) -> &'static str {
impl Callable for StringFunction {
fn name(&self) -> &'static str {
match self {
StringFunction::AsBytes => "as_bytes",
StringFunction::EndsWith => "ends_with",
@ -77,7 +79,7 @@ impl StringFunction {
}
}
pub fn r#type(&self) -> Type {
fn r#type(&self) -> Type {
match self {
StringFunction::AsBytes => {
Type::function(vec![Type::String], Type::list(Type::Integer))
@ -163,7 +165,7 @@ impl StringFunction {
}
}
pub fn call(&self, arguments: &[Value], _source: &str, _outer_context: &Map) -> Result<Value> {
fn call(&self, arguments: &[Value], _source: &str, _outer_context: &Map) -> Result<Value> {
let value = match self {
StringFunction::AsBytes => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;

View File

@ -4,11 +4,12 @@ use clap::{Parser, Subcommand};
use crossterm::event::{KeyCode, KeyModifiers};
use nu_ansi_term::Style;
use reedline::{
default_emacs_keybindings, DefaultPrompt, DefaultPromptSegment, EditCommand, Emacs,
Highlighter, Reedline, ReedlineEvent, Signal, SqliteBackedHistory, StyledText,
default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultHinter, EditCommand, Emacs,
Highlighter, Prompt, Reedline, ReedlineEvent, ReedlineMenu, Signal, SqliteBackedHistory,
StyledText,
};
use std::{fs::read_to_string, path::PathBuf};
use std::{borrow::Cow, fs::read_to_string, path::PathBuf, time::SystemTime};
use dust_lang::{built_in_values, Interpreter, Map, Result, Value};
@ -170,12 +171,41 @@ impl Highlighter for DustHighlighter {
}
}
struct DustPrompt;
impl Prompt for DustPrompt {
fn render_prompt_left(&self) -> Cow<str> {
let path = std::env::current_dir()
.map(|path| path.file_name().unwrap().to_string_lossy().to_string())
.unwrap_or_else(|_| "No workdir".to_string());
Cow::Owned(path)
}
fn render_prompt_right(&self) -> Cow<str> {
let time = humantime::format_rfc3339_seconds(SystemTime::now()).to_string();
Cow::Owned(time)
}
fn render_prompt_indicator(&self, _prompt_mode: reedline::PromptEditMode) -> Cow<str> {
Cow::Borrowed(" > ")
}
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
Cow::Borrowed(" > ")
}
fn render_prompt_history_search_indicator(
&self,
_history_search: reedline::PromptHistorySearch,
) -> Cow<str> {
Cow::Borrowed(" ? ")
}
}
fn run_shell(context: Map) -> Result<()> {
let mut interpreter = Interpreter::new(context.clone());
let mut prompt = DefaultPrompt::default();
prompt.left_prompt = DefaultPromptSegment::Basic(">".to_string());
let mut keybindings = default_emacs_keybindings();
keybindings.add_binding(
@ -184,7 +214,7 @@ fn run_shell(context: Map) -> Result<()> {
ReedlineEvent::Edit(vec![EditCommand::InsertNewline]),
);
keybindings.add_binding(
KeyModifiers::CONTROL,
KeyModifiers::NONE,
KeyCode::Tab,
ReedlineEvent::UntilFound(vec![
ReedlineEvent::Menu("completion_menu".to_string()),
@ -192,7 +222,7 @@ fn run_shell(context: Map) -> Result<()> {
]),
);
keybindings.add_binding(
KeyModifiers::NONE,
KeyModifiers::CONTROL,
KeyCode::Tab,
ReedlineEvent::Edit(vec![EditCommand::InsertString(" ".to_string())]),
);
@ -202,10 +232,20 @@ fn run_shell(context: Map) -> Result<()> {
SqliteBackedHistory::with_file(PathBuf::from("target/history"), None, None)
.expect("Error loading history."),
);
let hinter = Box::new(DefaultHinter::default());
let mut line_editor = Reedline::create()
.with_edit_mode(edit_mode)
.with_history(history)
.with_highlighter(Box::new(DustHighlighter::new(context)));
.with_highlighter(Box::new(DustHighlighter::new(context)))
.with_hinter(hinter)
.with_menu(ReedlineMenu::WithCompleter {
menu: Box::new(ColumnarMenu::default().with_name("completion_menu")),
completer: Box::new(DefaultCompleter::new_with_wordlen(
vec!["test".to_string()],
2,
)),
});
let prompt = DustPrompt;
loop {
let sig = line_editor.read_line(&prompt);

View File

@ -5,7 +5,9 @@ use std::{
use serde::{Deserialize, Serialize};
use crate::{BuiltInFunction, Format, FunctionNode, Map, Result, Type, Value};
use crate::{
built_in_functions::Callable, BuiltInFunction, Format, FunctionNode, Map, Result, Type, Value,
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum Function {
@ -20,16 +22,6 @@ impl Function {
built_in_function.call(arguments, source, outer_context)
}
Function::ContextDefined(function_node) => {
function_node.set(
"self".to_string(),
Value::Function(Function::ContextDefined(Arc::new(FunctionNode::new(
function_node.parameters().clone(),
function_node.body().clone(),
function_node.r#type().clone(),
*function_node.syntax_position(),
)))),
)?;
function_node.call(arguments, source, outer_context)
}
}