use crate::errors::*;
use crate::names::Name;
use crate::qualified_names::QualifiedName;
use crate::strings::ToFeelString;
use crate::value_null;
use crate::values::Value;
use dsntk_common::{DsntkError, Jsonify, Result};
use std::collections::btree_map::Iter;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::fmt;
use std::ops::Deref;
type FeelContextEntries = BTreeMap<Name, Value>;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct FeelContext(FeelContextEntries);
impl Deref for FeelContext {
type Target = FeelContextEntries;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<Value> for FeelContext {
type Error = DsntkError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
let Value::Context(ctx) = value else {
return Err(err_value_is_not_a_context(&value));
};
Ok(ctx)
}
}
impl From<FeelContext> for Value {
fn from(ctx: FeelContext) -> Self {
Value::Context(ctx)
}
}
impl fmt::Display for FeelContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{{{}}}",
self
.0
.iter()
.map(|(name, value)| { format!("{}: {}", if name.is_empty() { r#""""#.to_string() } else { name.to_string() }, value) })
.collect::<Vec<String>>()
.join(", ")
)
}
}
impl ToFeelString for FeelContext {
fn to_feel_string(&self) -> String {
format!(
"{{{}}}",
self
.0
.iter()
.map(|(name, value)| {
let name_str = format!("{name}");
let padded_name_str = match name_str.as_str() {
"{" | "}" | ":" | "," => format!("\"{name_str}\""),
"\"" => "\"\\\"\"".to_string(),
_ => name_str,
};
format!(r#"{padded_name_str}: {value}"#)
})
.collect::<Vec<String>>()
.join(", ")
)
}
}
impl Jsonify for FeelContext {
fn jsonify(&self) -> String {
format!(
"{{{}}}",
self
.0
.iter()
.map(|(name, value)| format!(r#""{}": {}"#, name, value.jsonify()))
.collect::<Vec<String>>()
.join(", ")
)
}
}
impl FeelContext {
pub fn new() -> Self {
Self::default()
}
pub fn contains_entry(&self, name: &Name) -> bool {
self.0.contains_key(name)
}
pub fn contains_entries(&self, qname: &QualifiedName) -> bool {
self.contains_deep(qname.as_slice())
}
pub fn get_entry(&self, name: &Name) -> Option<&Value> {
self.0.get(name)
}
pub fn set_entry(&mut self, name: &Name, value: Value) {
self.0.insert(name.clone(), value);
}
pub fn remove_entry(&mut self, name: &Name) -> Option<Value> {
self.0.remove(name)
}
pub fn set_null(&mut self, name: Name) {
self.0.insert(name, value_null!());
}
pub fn get_entries(&self) -> Vec<(&Name, &Value)> {
self.0.iter().collect::<Vec<(&Name, &Value)>>()
}
pub fn iter(&self) -> Iter<Name, Value> {
self.0.iter()
}
pub fn get_first(&self) -> Option<&Value> {
self.0.values().next()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_context(&self, name: &Name) -> bool {
matches!(self.0.get(name), Some(Value::Context(_)))
}
pub fn zip(&mut self, other: &FeelContext) {
for (name, value) in &other.0 {
self.0.insert(name.clone(), value.clone());
}
}
pub fn overwrite(&mut self, other: &FeelContext) {
for (name, value) in &other.0 {
if self.0.contains_key(name) {
self.0.insert(name.clone(), value.clone());
}
}
}
pub fn move_entry(&mut self, name: Name, parent: Name) {
if let Some(value) = self.0.remove(&name) {
self.create_entries(&[parent, name], value);
}
}
pub fn create_entry(&mut self, qname: &QualifiedName, value: Value) {
self.create_entries(qname.as_slice(), value);
}
pub fn search_entry<'a>(&'a self, qname: &'a QualifiedName) -> Option<&'a Value> {
self.search_deep(qname.as_slice())
}
pub fn contains_deep(&self, names: &[Name]) -> bool {
if names.is_empty() {
return false;
}
let tail = &names[1..];
if let Some(value) = self.0.get(&names[0]) {
if tail.is_empty() {
return true;
}
if let Value::Context(context) = value {
return context.contains_deep(tail);
}
}
false
}
pub fn create_entries(&mut self, names: &[Name], value: Value) {
if names.is_empty() {
return;
}
let key = names[0].clone();
let tail = &names[1..];
if tail.is_empty() {
self.0.insert(key, value);
return;
}
if let Some(Value::Context(ctx)) = self.0.get_mut(&key) {
ctx.create_entries(tail, value);
return;
}
let mut ctx = FeelContext::default();
ctx.create_entries(tail, value);
self.0.insert(key, ctx.into());
}
pub fn apply_entries(&mut self, names: &[Name], value: Value) -> Result<()> {
if names.is_empty() {
return Ok(());
}
let key = names[0].clone();
let tail = &names[1..];
if tail.is_empty() {
self.0.insert(key, value);
return Ok(());
}
match self.0.get_mut(&key) {
Some(Value::Context(ctx)) => {
ctx.apply_entries(tail, value)?;
return Ok(());
}
Some(other) => {
return Err(err_value_is_not_a_context(other));
}
_ => {}
}
let mut ctx = FeelContext::default();
ctx.apply_entries(tail, value)?;
self.0.insert(key, ctx.into());
Ok(())
}
pub fn search_deep(&self, names: &[Name]) -> Option<&Value> {
if !names.is_empty() {
let tail = &names[1..];
if let Some(value) = self.0.get(&names[0]) {
if let Value::Context(ctx) = value {
return if tail.is_empty() { Some(value) } else { ctx.search_deep(tail) };
} else if tail.is_empty() {
return Some(value);
}
}
}
None
}
}