shape_runtime/project/
sandbox.rs1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
8pub struct SandboxSection {
9 #[serde(default)]
11 pub enabled: bool,
12 #[serde(default)]
14 pub deterministic: bool,
15 #[serde(default)]
17 pub seed: Option<u64>,
18 #[serde(default)]
20 pub memory_limit: Option<String>,
21 #[serde(default)]
23 pub time_limit: Option<String>,
24 #[serde(default)]
26 pub virtual_fs: bool,
27 #[serde(default)]
29 pub seed_files: HashMap<String, String>,
30}
31
32impl SandboxSection {
33 pub fn memory_limit_bytes(&self) -> Option<u64> {
35 self.memory_limit.as_ref().and_then(|s| parse_byte_size(s))
36 }
37
38 pub fn time_limit_ms(&self) -> Option<u64> {
40 self.time_limit.as_ref().and_then(|s| parse_duration_ms(s))
41 }
42}
43
44pub(crate) fn parse_byte_size(s: &str) -> Option<u64> {
46 let s = s.trim();
47 let (num_part, suffix) = split_numeric_suffix(s)?;
48 let value: u64 = num_part.parse().ok()?;
49 let multiplier = match suffix.to_uppercase().as_str() {
50 "B" | "" => 1,
51 "KB" | "K" => 1024,
52 "MB" | "M" => 1024 * 1024,
53 "GB" | "G" => 1024 * 1024 * 1024,
54 _ => return None,
55 };
56 Some(value * multiplier)
57}
58
59pub(crate) fn parse_duration_ms(s: &str) -> Option<u64> {
61 let s = s.trim();
62 let (num_part, suffix) = split_numeric_suffix(s)?;
63 let value: u64 = num_part.parse().ok()?;
64 let multiplier = match suffix.to_lowercase().as_str() {
65 "ms" => 1,
66 "s" | "" => 1000,
67 "m" | "min" => 60_000,
68 _ => return None,
69 };
70 Some(value * multiplier)
71}
72
73fn split_numeric_suffix(s: &str) -> Option<(&str, &str)> {
75 let idx = s
76 .find(|c: char| !c.is_ascii_digit() && c != '.')
77 .unwrap_or(s.len());
78 if idx == 0 {
79 return None;
80 }
81 Some((&s[..idx], &s[idx..]))
82}