Skip to content

Commit 07f5056

Browse files
committed
refactor to a different extension
1 parent 97c55ca commit 07f5056

4 files changed

Lines changed: 163 additions & 44 deletions

File tree

src/evaluator.rs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ use std::ffi::{CStr, CString};
1414
use std::fmt::Write;
1515
use std::num::ParseIntError;
1616
use std::os::raw::c_char;
17-
use time::OffsetDateTime;
18-
use time::format_description::well_known::Rfc3339;
1917

2018
type PrimitiveFunction = unsafe extern "C" fn(*mut s7_scheme, s7_pointer) -> s7_pointer;
2119

@@ -172,7 +170,6 @@ impl Evaluator {
172170
primitive_expression_to_byte_vector(),
173171
primitive_byte_vector_to_expression(),
174172
primitive_random_byte_vector(),
175-
primitive_time_utc(),
176173
primitive_print(),
177174
];
178175

@@ -452,46 +449,6 @@ fn primitive_random_byte_vector() -> Primitive {
452449
)
453450
}
454451

455-
fn primitive_time_utc() -> Primitive {
456-
unsafe extern "C" fn code(sc: *mut s7_scheme, _args: s7_pointer) -> s7_pointer {
457-
match OffsetDateTime::now_utc().format(&Rfc3339) {
458-
Ok(timestamp) => match CString::new(timestamp) {
459-
Ok(c_timestamp) => s7_make_string(sc, c_timestamp.as_ptr()),
460-
Err(_) => s7_error(
461-
sc,
462-
s7_make_symbol(sc, c"time-error".as_ptr()),
463-
s7_list(
464-
sc,
465-
1,
466-
s7_make_string(
467-
sc,
468-
c"Generated UTC timestamp had invalid bytes".as_ptr(),
469-
),
470-
),
471-
),
472-
},
473-
Err(_) => s7_error(
474-
sc,
475-
s7_make_symbol(sc, c"time-error".as_ptr()),
476-
s7_list(
477-
sc,
478-
1,
479-
s7_make_string(sc, c"Failed to format UTC time".as_ptr()),
480-
),
481-
),
482-
}
483-
}
484-
485-
Primitive::new(
486-
code,
487-
c"time-utc",
488-
c"(time-utc) returns current UTC time as an RFC3339 timestamp",
489-
0,
490-
0,
491-
false,
492-
)
493-
}
494-
495452
fn primitive_print() -> Primitive {
496453
unsafe extern "C" fn code(sc: *mut s7_scheme, args: s7_pointer) -> s7_pointer {
497454
let mut result = String::new();

src/extensions/system.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use crate::evaluator as s7;
2+
use crate::evaluator::Primitive;
3+
use std::ffi::{CStr, CString};
4+
use time::OffsetDateTime;
5+
use time::format_description::well_known::Rfc3339;
6+
7+
pub fn primitive_s7_system_time_utc() -> Primitive {
8+
unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
9+
unsafe {
10+
let dt = if s7::s7_is_null(sc, args) {
11+
OffsetDateTime::now_utc()
12+
} else {
13+
let arg = s7::s7_car(args);
14+
15+
if !s7::s7_is_integer(arg) {
16+
return s7::s7_wrong_type_arg_error(
17+
sc,
18+
c"system-time-utc".as_ptr(),
19+
1,
20+
arg,
21+
c"a unix timestamp integer".as_ptr(),
22+
);
23+
}
24+
25+
match OffsetDateTime::from_unix_timestamp(s7::s7_integer(arg)) {
26+
Ok(value) => value,
27+
Err(_) => {
28+
return s7::s7_error(
29+
sc,
30+
s7::s7_make_symbol(sc, c"time-error".as_ptr()),
31+
s7::s7_list(
32+
sc,
33+
1,
34+
s7::s7_make_string(sc, c"Invalid unix timestamp range".as_ptr()),
35+
),
36+
);
37+
}
38+
}
39+
};
40+
41+
match dt.format(&Rfc3339) {
42+
Ok(timestamp) => match CString::new(timestamp) {
43+
Ok(c_timestamp) => s7::s7_make_string(sc, c_timestamp.as_ptr()),
44+
Err(_) => s7::s7_error(
45+
sc,
46+
s7::s7_make_symbol(sc, c"time-error".as_ptr()),
47+
s7::s7_list(
48+
sc,
49+
1,
50+
s7::s7_make_string(
51+
sc,
52+
c"Generated UTC timestamp had invalid bytes".as_ptr(),
53+
),
54+
),
55+
),
56+
},
57+
Err(_) => s7::s7_error(
58+
sc,
59+
s7::s7_make_symbol(sc, c"time-error".as_ptr()),
60+
s7::s7_list(
61+
sc,
62+
1,
63+
s7::s7_make_string(sc, c"Failed to format UTC time".as_ptr()),
64+
),
65+
),
66+
}
67+
}
68+
}
69+
70+
Primitive::new(
71+
code,
72+
c"system-time-utc",
73+
c"(system-time-utc [unix]) returns current UTC time or converts unix epoch seconds to RFC3339 UTC",
74+
0,
75+
1,
76+
false,
77+
)
78+
}
79+
80+
pub fn primitive_s7_system_time_unix() -> Primitive {
81+
unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
82+
unsafe {
83+
if s7::s7_is_null(sc, args) {
84+
return s7::s7_make_integer(sc, OffsetDateTime::now_utc().unix_timestamp());
85+
}
86+
87+
let arg = s7::s7_car(args);
88+
if !s7::s7_is_string(arg) {
89+
return s7::s7_wrong_type_arg_error(
90+
sc,
91+
c"system-time-unix".as_ptr(),
92+
1,
93+
arg,
94+
c"an RFC3339 UTC string".as_ptr(),
95+
);
96+
}
97+
98+
let s7_c_str = s7::s7_string(arg);
99+
let timestamp = match CStr::from_ptr(s7_c_str).to_str() {
100+
Ok(value) => value,
101+
Err(_) => {
102+
return s7::s7_error(
103+
sc,
104+
s7::s7_make_symbol(sc, c"encoding-error".as_ptr()),
105+
s7::s7_list(
106+
sc,
107+
1,
108+
s7::s7_make_string(sc, c"Invalid UTF-8 timestamp string".as_ptr()),
109+
),
110+
);
111+
}
112+
};
113+
114+
match OffsetDateTime::parse(timestamp, &Rfc3339) {
115+
Ok(dt) => s7::s7_make_integer(sc, dt.unix_timestamp()),
116+
Err(_) => s7::s7_error(
117+
sc,
118+
s7::s7_make_symbol(sc, c"time-error".as_ptr()),
119+
s7::s7_list(
120+
sc,
121+
1,
122+
s7::s7_make_string(sc, c"Failed to parse RFC3339 timestamp".as_ptr()),
123+
),
124+
),
125+
}
126+
}
127+
}
128+
129+
Primitive::new(
130+
code,
131+
c"system-time-unix",
132+
c"(system-time-unix [utc]) returns current unix epoch seconds or converts RFC3339 UTC to unix seconds",
133+
0,
134+
1,
135+
false,
136+
)
137+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::evaluator::{Evaluator, Primitive, Type, json2lisp, obj2str, lisp2json
55
use crate::extensions::crypto::{
66
primitive_s7_crypto_generate, primitive_s7_crypto_sign, primitive_s7_crypto_verify,
77
};
8+
use crate::extensions::system::{primitive_s7_system_time_unix, primitive_s7_system_time_utc};
89
use crate::persistor::{MemoryPersistor, PERSISTOR, Persistor, PersistorAccessError};
910
pub use crate::persistor::{SIZE, Word};
1011
use libc;
@@ -24,6 +25,7 @@ pub mod evaluator;
2425
mod persistor;
2526
mod extensions {
2627
pub mod crypto;
28+
pub mod system;
2729
}
2830

2931
pub static JOURNAL: Lazy<Journal> = Lazy::new(|| Journal::new());
@@ -303,6 +305,8 @@ impl Journal {
303305
primitive_s7_crypto_generate(),
304306
primitive_s7_crypto_sign(),
305307
primitive_s7_crypto_verify(),
308+
primitive_s7_system_time_unix(),
309+
primitive_s7_system_time_utc(),
306310
],
307311
);
308312

tests/record.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,33 @@ fn test_trailing_comment_at_end() {
5757

5858
#[test]
5959
fn test_time_utc_format() {
60-
let result = JOURNAL.evaluate("(time-utc)");
60+
let result = JOURNAL.evaluate("(system-time-utc)");
6161
assert!(result.starts_with('\"'));
6262
assert!(result.ends_with("Z\""));
6363
assert!(result.contains('T'));
6464
}
6565

66+
#[test]
67+
fn test_time_unix_format() {
68+
let result = JOURNAL.evaluate("(system-time-unix)");
69+
let parsed = result
70+
.parse::<i64>()
71+
.expect("system-time-unix should return an integer");
72+
assert!(parsed > 0, "system-time-unix should be a positive epoch value");
73+
}
74+
75+
#[test]
76+
fn test_time_utc_from_unix() {
77+
let result = JOURNAL.evaluate("(system-time-utc 0)");
78+
assert_eq!(result, "\"1970-01-01T00:00:00Z\"");
79+
}
80+
81+
#[test]
82+
fn test_time_unix_from_utc() {
83+
let result = JOURNAL.evaluate("(system-time-unix \"1970-01-01T00:00:00Z\")");
84+
assert_eq!(result, "0");
85+
}
86+
6687
#[test]
6788
fn test_scratch() {
6889
let assert = setup();

0 commit comments

Comments
 (0)