harness_context/
runtime.rs1use async_trait::async_trait;
4use harness_core::{Clock, KvStore, ProcessOutput, ProcessRunner};
5use std::collections::HashMap;
6use std::path::Path;
7use std::sync::Mutex;
8
9pub struct SystemClock;
11
12impl Clock for SystemClock {
13 fn now_ms(&self) -> i64 {
14 std::time::SystemTime::now()
15 .duration_since(std::time::UNIX_EPOCH)
16 .map(|d| d.as_millis() as i64)
17 .unwrap_or(0)
18 }
19}
20
21pub struct TokioRunner;
23
24#[async_trait]
25impl ProcessRunner for TokioRunner {
26 async fn exec(
27 &self,
28 program: &str,
29 args: &[&str],
30 cwd: Option<&Path>,
31 ) -> std::io::Result<ProcessOutput> {
32 let mut cmd = tokio::process::Command::new(program);
33 cmd.args(args);
34 if let Some(c) = cwd {
35 cmd.current_dir(c);
36 }
37 let out = cmd.output().await?;
38 Ok(ProcessOutput {
39 status: out.status.code().unwrap_or(-1),
40 stdout: String::from_utf8_lossy(&out.stdout).to_string(),
41 stderr: String::from_utf8_lossy(&out.stderr).to_string(),
42 })
43 }
44}
45
46pub struct InMemoryKv {
48 inner: Mutex<HashMap<String, Vec<u8>>>,
49}
50
51impl InMemoryKv {
52 pub fn new() -> Self {
53 Self {
54 inner: Mutex::new(HashMap::new()),
55 }
56 }
57}
58
59impl Default for InMemoryKv {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65#[async_trait]
66impl KvStore for InMemoryKv {
67 async fn get(&self, key: &str) -> Option<Vec<u8>> {
68 self.inner.lock().ok()?.get(key).cloned()
69 }
70 async fn set(&self, key: &str, value: Vec<u8>) {
71 if let Ok(mut g) = self.inner.lock() {
72 g.insert(key.to_string(), value);
73 }
74 }
75 async fn delete(&self, key: &str) {
76 if let Ok(mut g) = self.inner.lock() {
77 g.remove(key);
78 }
79 }
80}