koto_runtime/core_lib/
os.rs1mod command;
4
5use self::command::Command;
6use crate::{derive::*, prelude::*, Result};
7use chrono::prelude::*;
8use instant::Instant;
9
10pub fn make_module() -> KMap {
12 use KValue::Number;
13
14 let result = KMap::with_type("core.os");
15
16 result.add_fn("command", |ctx| match ctx.args() {
17 [KValue::Str(command)] => Ok(Command::make_value(command)),
18 unexpected => unexpected_args("|String|", unexpected),
19 });
20
21 result.add_fn("name", |ctx| match ctx.args() {
22 [] => Ok(std::env::consts::OS.into()),
23 unexpected => unexpected_args("||", unexpected),
24 });
25
26 result.add_fn("process_id", |ctx| match ctx.args() {
27 [] => {
28 #[cfg(target_arch = "wasm32")]
29 {
30 return runtime_error!(crate::ErrorKind::UnsupportedPlatform);
32 }
33
34 #[cfg(not(target_arch = "wasm32"))]
35 {
36 Ok(std::process::id().into())
37 }
38 }
39 unexpected => unexpected_args("||", unexpected),
40 });
41
42 result.add_fn("start_timer", |ctx| match ctx.args() {
43 [] => Ok(Timer::now()),
44 unexpected => unexpected_args("||", unexpected),
45 });
46
47 result.add_fn("time", |ctx| match ctx.args() {
48 [] => Ok(DateTime::now()),
49 [Number(seconds)] => DateTime::from_seconds(seconds.into(), None),
50 [Number(seconds), Number(offset)] => {
51 DateTime::from_seconds(seconds.into(), Some(offset.into()))
52 }
53 unexpected => unexpected_args("||, or |Number|, or |Number, Number|", unexpected),
54 });
55
56 result
57}
58
59#[derive(Clone, Debug, KotoCopy, KotoType)]
61pub struct DateTime(chrono::DateTime<FixedOffset>);
62
63#[koto_impl(runtime = crate)]
64impl DateTime {
65 fn with_chrono_datetime(time: chrono::DateTime<FixedOffset>) -> KValue {
66 KObject::from(Self(time)).into()
67 }
68
69 fn now() -> KValue {
70 Self::with_chrono_datetime(Local::now().fixed_offset())
71 }
72
73 fn from_seconds(seconds: f64, maybe_offset: Option<i64>) -> Result<KValue> {
74 let seconds_i64 = seconds as i64;
75 let sub_nanos = (seconds.fract() * 1.0e9) as u32;
76 match chrono::DateTime::from_timestamp(seconds_i64, sub_nanos) {
77 Some(utc) => {
78 let offset = match maybe_offset {
79 Some(offset) => match FixedOffset::east_opt(offset as i32) {
80 Some(offset) => offset,
81 None => return runtime_error!("time offset is out of range: {offset}"),
82 },
83 None => *Local::now().offset(),
84 };
85 let local = utc.with_timezone(&offset);
86 Ok(Self::with_chrono_datetime(local))
87 }
88 None => runtime_error!("timestamp in seconds is out of range: {seconds}"),
89 }
90 }
91
92 #[koto_method]
93 fn day(&self) -> KValue {
94 self.0.day().into()
95 }
96
97 #[koto_method]
98 fn hour(&self) -> KValue {
99 self.0.hour().into()
100 }
101
102 #[koto_method]
103 fn minute(&self) -> KValue {
104 self.0.minute().into()
105 }
106
107 #[koto_method]
108 fn month(&self) -> KValue {
109 self.0.month().into()
110 }
111
112 #[koto_method]
113 fn second(&self) -> KValue {
114 self.0.second().into()
115 }
116
117 #[koto_method]
118 fn nanosecond(&self) -> KValue {
119 self.0.nanosecond().into()
120 }
121
122 #[koto_method]
123 fn timestamp(&self) -> KValue {
124 let seconds = self.0.timestamp() as f64;
125 let sub_nanos = self.0.timestamp_subsec_nanos();
126 (seconds + sub_nanos as f64 / 1.0e9).into()
127 }
128
129 #[koto_method]
130 fn timezone_offset(&self) -> KValue {
131 self.0.offset().local_minus_utc().into()
132 }
133
134 #[koto_method]
135 fn timezone_string(&self) -> KValue {
136 self.0.format("%z").to_string().into()
137 }
138
139 #[koto_method]
140 fn year(&self) -> KValue {
141 self.0.year().into()
142 }
143}
144
145impl KotoObject for DateTime {
146 fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
147 ctx.append(self.0.format("%F %T").to_string());
148 Ok(())
149 }
150}
151
152#[derive(Clone, Debug, KotoCopy, KotoType)]
154pub struct Timer(Instant);
155
156#[koto_impl(runtime = crate)]
157impl Timer {
158 fn now() -> KValue {
159 let timer = Self(Instant::now());
160 KObject::from(timer).into()
161 }
162
163 fn elapsed_seconds(&self) -> f64 {
164 self.0.elapsed().as_secs_f64()
165 }
166
167 #[koto_method]
168 fn elapsed(&self) -> KValue {
169 self.elapsed_seconds().into()
170 }
171}
172
173impl KotoObject for Timer {
174 fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
175 ctx.append(format!("Timer({:.3}s)", self.elapsed_seconds()));
176 Ok(())
177 }
178
179 fn subtract(&self, rhs: &KValue) -> Result<KValue> {
180 match rhs {
181 KValue::Object(o) if o.is_a::<Self>() => {
182 let rhs = o.cast::<Self>().unwrap();
183
184 let result = if self.0 >= rhs.0 {
185 self.0.duration_since(rhs.0).as_secs_f64()
186 } else {
187 -(rhs.0.duration_since(self.0).as_secs_f64())
188 };
189
190 Ok(result.into())
191 }
192 unexpected => unexpected_type(Self::type_static(), unexpected),
193 }
194 }
195}