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

add time utils and test #263

Merged
merged 1 commit into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,109 changes: 559 additions & 550 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions csml_interpreter/CSML/basic_test/built-in/time.csml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
start:
do time = Time()

say time.milliseconds.is_int()
say time.unix().is_int()

do time.at(2014, 10, 20)
say time.format()

say time.format("%Y")
goto end
1 change: 1 addition & 0 deletions csml_interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ nom_locate = "2.1.0"
nom = "5.1.2"
rand = "0.7.3"
jsonwebtoken = "7"
chrono = "0.4.19"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
libc = "0.2.79"
Expand Down
2 changes: 2 additions & 0 deletions csml_interpreter/src/data/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub enum ContentType {
Hex,
Jwt,
Crypto,
Time,
Primitive,
}

Expand Down Expand Up @@ -72,6 +73,7 @@ impl ContentType {
"hex" => ContentType::Hex,
"jwt" => ContentType::Jwt,
"crypto" => ContentType::Crypto,
"time" => ContentType::Time,
"event" => ContentType::Event(String::from("")),
_ => ContentType::Primitive,
}
Expand Down
1 change: 1 addition & 0 deletions csml_interpreter/src/data/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod object;
pub mod string;

pub mod tools;
pub mod tools_time;
pub mod tools_crypto;
pub mod tools_jwt;

Expand Down
147 changes: 146 additions & 1 deletion csml_interpreter/src/data/primitive/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::data::{
literal::ContentType,
message::Message,
primitive::{
tools_crypto, tools_jwt, Data, MessageData, Primitive, PrimitiveArray, PrimitiveBoolean,
tools_time, tools_crypto, tools_jwt, Data, MessageData, Primitive, PrimitiveArray, PrimitiveBoolean,
PrimitiveInt, PrimitiveNull, PrimitiveString, PrimitiveType, Right, MSG,
},
tokens::TYPES,
Expand All @@ -16,6 +16,7 @@ use crate::interpreter::{
builtins::http::http_request, json_to_rust::json_to_literal,
variable_handler::match_literals::match_obj,
};
use chrono::{DateTime, TimeZone, Utc, SecondsFormat};
use lazy_static::*;
use regex::Regex;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -67,6 +68,29 @@ lazy_static! {
};
}

lazy_static! {
static ref FUNCTIONS_TIME: HashMap<&'static str, (PrimitiveMethod, Right)> = {
let mut map = HashMap::new();

map.insert(
"at",
(PrimitiveObject::set_date_at as PrimitiveMethod, Right::Read),
);

map.insert(
"unix",
(PrimitiveObject::unix as PrimitiveMethod, Right::Read),
);

map.insert(
"format",
(PrimitiveObject::date_format as PrimitiveMethod, Right::Read),
);

map
};
}

lazy_static! {
static ref FUNCTIONS_JWT: HashMap<&'static str, (PrimitiveMethod, Right)> = {
let mut map = HashMap::new();
Expand Down Expand Up @@ -549,6 +573,125 @@ impl PrimitiveObject {
}
}

impl PrimitiveObject {
fn set_date_at(
object: &mut PrimitiveObject,
args: &HashMap<String, Literal>,
_data: &mut Data,
interval: Interval,
_content_type: &str,
) -> Result<Literal, ErrorInfo> {
let date = tools_time::get_date(args);

let date = Utc.ymd(
date[0] as i32, // year
date[1] as u32, // month
date[2] as u32 // day
).and_hms_milli_opt(
date[3] as u32, // hour
date[4] as u32, // min
date[5] as u32, // sec
date[6] as u32, // milli
);

match date {
Some(date) => {
object.value.insert(
"milliseconds".to_owned(),
PrimitiveInt::get_literal(
date.timestamp_millis(),
interval
)
);
Ok(PrimitiveBoolean::get_literal(true, interval))
}
None => Ok(PrimitiveBoolean::get_literal(false, interval))
}
}

fn unix(
object: &mut PrimitiveObject,
_args: &HashMap<String, Literal>,
data: &mut Data,
interval: Interval,
_content_type: &str,
) -> Result<Literal, ErrorInfo> {
let usage = "invalid value, use 'Time()' built-in to create a valid 'time' object";

match object.value.get("milliseconds") {
Some(lit) if lit.primitive.get_type() == PrimitiveType::PrimitiveInt => {
let millis = Literal::get_value::<i64>(
&lit.primitive,
&data.context.flow,
interval,
"".to_string(),
)?;

let date: DateTime<Utc> = Utc.timestamp_millis(*millis);

Ok(PrimitiveInt::get_literal(date.timestamp_millis(), interval))
},
_ => {
return Err(gen_error_info(
Position::new(interval, &data.context.flow,),
format!("{}", usage),
))
}
}
}

fn date_format(
object: &mut PrimitiveObject,
args: &HashMap<String, Literal>,
data: &mut Data,
interval: Interval,
_content_type: &str,
) -> Result<Literal, ErrorInfo> {
let usage = "Time().format(format: String)";

let date: DateTime<Utc> = match object.value.get("milliseconds") {
Some(lit) if lit.primitive.get_type() == PrimitiveType::PrimitiveInt => {
let millis = Literal::get_value::<i64>(
&lit.primitive,
&data.context.flow,
interval,
"".to_string(),
)?;

Utc.timestamp_millis(*millis)
},
_ => {
return Err(gen_error_info(
Position::new(interval, &data.context.flow,),
format!("usage: {}", usage),
))
}
};

let formatted_date = match args.len() {
0 => {
date.to_rfc3339_opts(SecondsFormat::Millis, true)
}
_ => {
let format_lit = match args.get("arg0") {
Some(res) => res.to_owned(),
_ => PrimitiveNull::get_literal(Interval::default()),
};

let format = Literal::get_value::<String>(
&format_lit.primitive,
&data.context.flow,
interval,
"format parameter must be of type string".to_string(),
)?;
date.format(format).to_string()
}
};

Ok(PrimitiveString::get_literal(&formatted_date, interval))
}
}

impl PrimitiveObject {
fn jwt_sign(
object: &mut PrimitiveObject,
Expand Down Expand Up @@ -1833,6 +1976,7 @@ impl Primitive for PrimitiveObject {
let hex = vec![FUNCTIONS_HEX.clone()];
let jwt = vec![FUNCTIONS_JWT.clone()];
let crypto = vec![FUNCTIONS_CRYPTO.clone()];
let time = vec![FUNCTIONS_TIME.clone()];
let generics = vec![FUNCTIONS_READ.clone(), FUNCTIONS_WRITE.clone()];

let mut is_event = false;
Expand All @@ -1848,6 +1992,7 @@ impl Primitive for PrimitiveObject {
ContentType::Hex => ("", hex),
ContentType::Jwt => ("", jwt),
ContentType::Crypto => ("", crypto),
ContentType::Time => ("", time),
ContentType::Primitive => ("", generics),
};

Expand Down
36 changes: 36 additions & 0 deletions csml_interpreter/src/data/primitive/tools_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::data::{
Literal, primitive::PrimitiveType,
};
use std::{collections::HashMap};


////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

pub fn get_date(args: &HashMap<String, Literal>) -> [i64; 7] {

let mut date: [i64; 7] = [0; 7];

// set default month, day, and hour to 1 year dose not need to have a default
// value because set_date_at expect at least the year value as parameter
date[1] = 1; // month
date[2] = 1; // day
date[3] = 1; // hour

let len = args.len();

for index in 0..len {
match args.get(&format!("arg{}", index)) {
Some(lit) if lit.primitive.get_type() == PrimitiveType::PrimitiveInt => {
let value = serde_json::from_str(&lit.primitive.to_string()).unwrap();

date[index] = value;
},
_ => {},
}

}

date
}
3 changes: 2 additions & 1 deletion csml_interpreter/src/data/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,13 @@ pub const HEX: &str = "Hex";
pub const FILE: &str = "File";
pub const DEBUG: &str = "Debug";
pub const UUID: &str = "UUID";
pub const TIME: &str = "Time";

pub const OBJECT: &str = "Object";

pub const BUILT_IN: &[&str] = &[
ONE_OF, SHUFFLE, LENGTH, FIND, RANDOM, FLOOR, FN, APP, HTTP, OBJECT, DEBUG, UUID, BASE64, HEX,
JWT, CRYPTO,
JWT, CRYPTO, TIME
];

pub const FROM_FILE: &str = "FromFile";
Expand Down
4 changes: 4 additions & 0 deletions csml_interpreter/src/interpreter/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub mod format;
pub mod functions;
pub mod http;
pub mod jwt;
pub mod time;

pub mod tools;

use crate::data::{
Expand All @@ -19,6 +21,7 @@ use format::*;
use functions::*;
use http::http;
use jwt::jwt;
use time::time;

pub fn match_native_builtin(
name: &str,
Expand Down Expand Up @@ -59,6 +62,7 @@ pub fn match_builtin(
UUID => uuid_command(args, &data.context.flow, interval),
JWT => jwt(args, &data.context.flow, interval),
CRYPTO => crypto(args, &data.context.flow, interval),
TIME => time(args, &data.context.flow, interval),

//old builtin
_object => object(args, &data.context.flow, interval),
Expand Down
28 changes: 28 additions & 0 deletions csml_interpreter/src/interpreter/builtins/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::data::error_info::ErrorInfo;
use crate::data::primitive::{PrimitiveObject, PrimitiveInt};
use crate::data::{ast::Interval, ArgsType, Literal};
use std::{collections::HashMap};
use chrono::{Utc};

////////////////////////////////////////////////////////////////////////////////
/// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////

pub fn time(_args: ArgsType, _flow_name: &str, interval: Interval) -> Result<Literal, ErrorInfo> {
let mut time: HashMap<String, Literal> = HashMap::new();
let date = Utc::now();

time.insert(
"milliseconds".to_owned(),
PrimitiveInt::get_literal(
date.timestamp_millis(),
interval
)
);

let mut result = PrimitiveObject::get_literal(&time, interval);

result.set_content_type("time");

Ok(result)
}
32 changes: 32 additions & 0 deletions csml_interpreter/tests/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
mod support;

use csml_interpreter::data::context::Context;
use csml_interpreter::data::event::Event;
use std::collections::HashMap;

use crate::support::tools::format_message;
use crate::support::tools::message_to_json_value;

use serde_json::Value;

#[test]
fn ok_time() {
let data =
r#"{"messages":[
{"content":{"text": "true"},"content_type":"text"},
{"content":{"text": "true"},"content_type":"text"},
{"content":{"text": "2014-10-20T01:00:00.000Z"},"content_type":"text"},
{"content":{"text": "2014"},"content_type":"text"}
],
"memories":[]}"#;
let msg = format_message(
Event::new("payload", "", serde_json::json!({})),
Context::new(HashMap::new(), HashMap::new(), None, None, "start", "flow"),
"CSML/basic_test/built-in/time.csml",
);

let v1: Value = message_to_json_value(msg);
let v2: Value = serde_json::from_str(data).unwrap();

assert_eq!(v1, v2)
}