Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: velocity enforcement #237

Merged
merged 14 commits into from
Oct 7, 2024
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ start-deps:
docker compose up -d integration-deps

setup-db:
cd cala-ledger && cargo sqlx migrate run
cd cala-ledger && cargo sqlx migrate run || cargo sqlx migrate run
cd cala-server && cargo sqlx migrate run --ignore-missing

reset-deps: clean-deps start-deps setup-db
Expand Down
3 changes: 2 additions & 1 deletion cala-cel-interpreter/src/cel_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ pub enum CelType {
Bool,
Null,

// Addons
// Abstract
Date,
Timestamp,
Uuid,
Decimal,
}
6 changes: 3 additions & 3 deletions cala-cel-interpreter/src/context/decimal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use lazy_static::lazy_static;

use std::collections::HashMap;
use std::{borrow::Cow, collections::HashMap};

use crate::builtins;

Expand All @@ -10,11 +10,11 @@ lazy_static! {
pub static ref CEL_CONTEXT: CelContext = {
let mut idents = HashMap::new();
idents.insert(
SELF_PACKAGE_NAME.to_string(),
SELF_PACKAGE_NAME,
ContextItem::Function(Box::new(builtins::decimal::cast)),
);
idents.insert(
"Add".to_string(),
Cow::Borrowed("Add"),
ContextItem::Function(Box::new(builtins::decimal::add)),
);
CelContext { idents }
Expand Down
16 changes: 8 additions & 8 deletions cala-cel-interpreter/src/context/mod.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
mod decimal;

use std::collections::HashMap;
use std::{borrow::Cow, collections::HashMap};

use crate::{builtins, error::*, value::*};

const SELF_PACKAGE_NAME: &str = "self";
const SELF_PACKAGE_NAME: Cow<'static, str> = Cow::Borrowed("self");

type CelFunction = Box<dyn Fn(Vec<CelValue>) -> Result<CelValue, CelError> + Sync>;
#[derive(Debug)]
pub struct CelContext {
idents: HashMap<String, ContextItem>,
idents: HashMap<Cow<'static, str>, ContextItem>,
}

impl CelContext {
pub fn add_variable(&mut self, name: impl Into<String>, value: impl Into<CelValue>) {
pub fn add_variable(&mut self, name: impl Into<Cow<'static, str>>, value: impl Into<CelValue>) {
self.idents
.insert(name.into(), ContextItem::Value(value.into()));
}

pub fn new() -> Self {
let mut idents = HashMap::new();
idents.insert(
"date".to_string(),
Cow::Borrowed("date"),
ContextItem::Function(Box::new(builtins::date)),
);
idents.insert(
"uuid".to_string(),
Cow::Borrowed("uuid"),
ContextItem::Function(Box::new(builtins::uuid)),
);
idents.insert(
"decimal".to_string(),
Cow::Borrowed("decimal"),
ContextItem::Package(&decimal::CEL_CONTEXT),
);
Self { idents }
}

pub(crate) fn package_self(&self) -> Result<&ContextItem, CelError> {
self.lookup(SELF_PACKAGE_NAME)
self.lookup(&SELF_PACKAGE_NAME)
}

pub(crate) fn lookup(&self, name: &str) -> Result<&ContextItem, CelError> {
Expand Down
9 changes: 6 additions & 3 deletions cala-cel-interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ fn evaluate_member<'a>(
use ast::Member::*;
match member {
Attribute(name) => match target {
EvalType::Value(CelValue::Map(map)) => Ok(EvalType::Value(map.get(name))),
EvalType::ContextItem(ContextItem::Value(CelValue::Map(map))) => {
Ok(EvalType::Value(map.get(name)))
}
Expand Down Expand Up @@ -379,10 +380,12 @@ mod tests {

#[test]
fn lookup() {
let expression = "params.hello".parse::<CelExpression>().unwrap();
let mut context = CelContext::new();
let expression = "params.hello.world".parse::<CelExpression>().unwrap();
let mut hello = CelMap::new();
hello.insert("world", 42);
let mut params = CelMap::new();
params.insert("hello", 42);
params.insert("hello", hello);
let mut context = CelContext::new();
context.add_variable("params", params);
assert_eq!(expression.evaluate(&context).unwrap(), CelValue::Int(42));
}
Expand Down
38 changes: 36 additions & 2 deletions cala-cel-interpreter/src/value.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cel_parser::{ast::Literal, Expression};
use chrono::NaiveDate;
use chrono::{DateTime, NaiveDate, Utc};
use rust_decimal::Decimal;
use uuid::Uuid;

Expand All @@ -25,9 +25,10 @@ pub enum CelValue {
Bool(bool),
Null,

// Addons
// Abstract
Decimal(Decimal),
Date(NaiveDate),
Timestamp(DateTime<Utc>),
Uuid(Uuid),
}

Expand Down Expand Up @@ -226,6 +227,7 @@ impl From<&CelValue> for CelType {
CelValue::Decimal(_) => CelType::Decimal,
CelValue::Date(_) => CelType::Date,
CelValue::Uuid(_) => CelType::Uuid,
CelValue::Timestamp(_) => CelType::Timestamp,
}
}
}
Expand Down Expand Up @@ -269,6 +271,22 @@ impl<'a> TryFrom<&'a CelValue> for &'a Decimal {
}
}

impl<'a> TryFrom<CelResult<'a>> for bool {
type Error = ResultCoercionError;

fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
if let CelValue::Bool(b) = val {
Ok(b)
} else {
Err(ResultCoercionError::BadCoreTypeCoercion(
format!("{expr:?}"),
CelType::from(&val),
CelType::Bool,
))
}
}
}

impl<'a> TryFrom<CelResult<'a>> for NaiveDate {
type Error = ResultCoercionError;

Expand All @@ -285,6 +303,22 @@ impl<'a> TryFrom<CelResult<'a>> for NaiveDate {
}
}

impl<'a> TryFrom<CelResult<'a>> for DateTime<Utc> {
type Error = ResultCoercionError;

fn try_from(CelResult { expr, val }: CelResult) -> Result<Self, Self::Error> {
if let CelValue::Timestamp(d) = val {
Ok(d)
} else {
Err(ResultCoercionError::BadCoreTypeCoercion(
format!("{expr:?}"),
CelType::from(&val),
CelType::Timestamp,
))
}
}
}

impl<'a> TryFrom<CelResult<'a>> for Uuid {
type Error = ResultCoercionError;

Expand Down
18 changes: 18 additions & 0 deletions cala-ledger-core-types/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,21 @@ pub struct EntryValues {
pub direction: DebitOrCredit,
pub description: Option<String>,
}

mod cel {
use cel_interpreter::{CelMap, CelValue};

impl From<&super::EntryValues> for CelValue {
fn from(entry: &super::EntryValues) -> Self {
let mut map = CelMap::new();
map.insert("id", entry.id);
map.insert("entry_type", entry.entry_type.clone());
map.insert("sequence", CelValue::UInt(entry.sequence as u64));
map.insert("layer", entry.layer);
map.insert("direction", entry.direction);
map.insert("units", entry.units);
map.insert("currency", entry.currency);
map.into()
}
}
}
25 changes: 25 additions & 0 deletions cala-ledger-core-types/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ impl<'a> TryFrom<CelResult<'a>> for DebitOrCredit {
}
}

impl From<DebitOrCredit> for CelValue {
fn from(v: DebitOrCredit) -> Self {
match v {
DebitOrCredit::Debit => "DEBIT".into(),
DebitOrCredit::Credit => "CREDIT".into(),
}
}
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, sqlx::Type)]
#[sqlx(type_name = "Status", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
Expand Down Expand Up @@ -108,6 +117,16 @@ impl Default for Layer {
}
}

impl From<Layer> for CelValue {
fn from(l: Layer) -> Self {
match l {
Layer::Settled => "SETTLED".into(),
Layer::Pending => "PENDING".into(),
Layer::Encumbrance => "ENCUMBRANCE".into(),
}
}
}

#[derive(Debug, Clone, Copy, Eq, Serialize, Deserialize)]
#[serde(try_from = "String")]
#[serde(into = "&str")]
Expand All @@ -131,6 +150,12 @@ impl std::fmt::Display for Currency {
}
}

impl From<Currency> for CelValue {
fn from(c: Currency) -> Self {
c.code().into()
}
}

impl std::hash::Hash for Currency {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.code().hash(state);
Expand Down
19 changes: 19 additions & 0 deletions cala-ledger-core-types/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,22 @@ pub struct TransactionValues {
pub description: Option<String>,
pub metadata: Option<serde_json::Value>,
}

mod cel {
use cel_interpreter::{CelMap, CelValue};

impl From<&super::TransactionValues> for CelValue {
fn from(tx: &super::TransactionValues) -> Self {
let mut map = CelMap::new();
map.insert("id", tx.id);
map.insert("journal_id", tx.journal_id);
map.insert("tx_template_id", tx.tx_template_id);
map.insert("effective", tx.effective);
map.insert("correlation_id", tx.correlation_id.clone());
if let Some(metadata) = &tx.metadata {
map.insert("metadata", metadata.clone());
}
map.into()
}
}
}
34 changes: 34 additions & 0 deletions cala-ledger-core-types/src/velocity/balance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use rust_decimal::Decimal;

use crate::{balance::BalanceSnapshot, primitives::*};

#[derive(Debug, Clone, sqlx::Type, PartialEq, Eq, Hash)]
#[sqlx(transparent)]
pub struct Window(serde_json::Value);

impl Window {
pub fn inner(&self) -> &serde_json::Value {
&self.0
}
}

impl From<serde_json::Map<String, serde_json::Value>> for Window {
fn from(map: serde_json::Map<String, serde_json::Value>) -> Self {
Window(map.into())
}
}

impl From<serde_json::Value> for Window {
fn from(map: serde_json::Value) -> Self {
Window(map)
}
}

pub struct VelocityBalance {
pub control_id: VelocityControlId,
pub limit_id: VelocityLimitId,
pub spent: Decimal,
pub remaining: Decimal,
pub currency: Currency,
pub balance: BalanceSnapshot,
}
2 changes: 2 additions & 0 deletions cala-ledger-core-types/src/velocity/limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ pub struct BalanceLimit {
pub layer: CelExpression,
pub amount: CelExpression,
pub enforcement_direction: CelExpression,
pub start: Option<CelExpression>,
pub end: Option<CelExpression>,
}
2 changes: 2 additions & 0 deletions cala-ledger-core-types/src/velocity/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod balance;
mod control;
mod limit;

pub use balance::*;
pub use control::*;
pub use limit::*;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading