pub use std::path::{Path, PathBuf};
pub use std::result::Result as StdResult;
#[derive(Debug, Clone)]
pub struct Config {
pub version: String,
pub json_engine: JsonEngineConfig,
pub security: SecurityConfig,
}
#[derive(Debug, Clone)]
pub struct SecurityConfig {
pub encryption: EncryptionConfig,
pub rate_limiting: RateLimitingConfig,
}
#[derive(Debug, Clone)]
pub struct EncryptionConfig {
pub enabled: bool,
pub algorithm: String,
}
#[derive(Debug, Clone)]
pub struct RateLimitingConfig {
pub enabled: bool,
pub requests_per_minute: u64,
pub burst: u64,
}
#[derive(Debug, Clone)]
pub struct JsonEngineConfig {
pub schema_version: String,
pub strict_validation: bool,
pub output: OutputConfig,
pub aggregation: AggregationConfig,
pub training: TrainingConfig,
}
#[derive(Debug, Clone)]
pub struct OutputConfig {
pub base_path: String,
pub channels: Channels,
}
#[derive(Debug, Clone)]
pub struct Channels {
pub events: ChannelConfig,
pub quality_metrics: ChannelConfig,
pub training_samples: ChannelConfig,
pub aggregated_stats: AggregatedStatsConfig,
}
#[derive(Debug, Clone)]
pub struct ChannelConfig {
pub enabled: bool,
pub filename: String,
pub buffer_size: usize,
pub auto_flush_seconds: u64,
}
#[derive(Debug, Clone)]
pub struct AggregatedStatsConfig {
pub enabled: bool,
pub filename: String,
pub window_minutes: u64,
}
#[derive(Debug, Clone)]
pub struct AggregationConfig {
pub enabled: bool,
pub window_minutes: u64,
pub compute_percentiles: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct TrainingConfig {
pub auto_generate_samples: bool,
pub gold_threshold: f64,
pub silver_threshold: f64,
pub bronze_threshold: f64,
pub max_samples_per_day: u64,
pub require_user_validation_for_gold: bool,
}
pub fn civil_from_days(days: i64) -> (i32, u32, u32) {
let z = days + 719468;
let era = if z >= 0 {
z / 146097
} else {
(z - 146096) / 146097
};
let doe = z - era * 146097;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = (mp + 3) as i32 - if mp < 10 { 0 } else { 12 };
let year = (y as i32) + if m <= 2 { 1 } else { 0 };
(year, (m as u32), d as u32)
}
pub mod atomic {
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use native_neural_network_std::{digest_to_hex_lower, sha256_bytes};
use std::sync::atomic::{AtomicU64, Ordering};
static ID_COUNTER: AtomicU64 = AtomicU64::new(1);
pub fn current_time_rfc3339() -> String {
let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let secs = dur.as_secs() as i64;
let nsecs = dur.subsec_nanos();
let days = secs.div_euclid(86400);
let secs_of_day = secs.rem_euclid(86400);
let (year, month, day) = crate::config::civil_from_days(days);
let hour = (secs_of_day / 3600) as u32;
let minute = ((secs_of_day % 3600) / 60) as u32;
let second = (secs_of_day % 60) as u32;
if nsecs == 0 {
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
year, month, day, hour, minute, second
)
} else {
let mut frac = format!("{:09}", nsecs);
while frac.ends_with('0') {
frac.pop();
}
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{}Z",
year, month, day, hour, minute, second, frac
)
}
}
pub fn atomic_write(path: &Path, bytes: &[u8]) -> io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let tmp_path = tmp_path_for(path);
let mut options = OpenOptions::new();
options.create(true).write(true).truncate(true);
let mut file: File = options.open(&tmp_path)?;
file.write_all(bytes)?;
file.flush()?;
file.sync_all()?;
fs::rename(&tmp_path, path)?;
if let Some(parent) = path.parent() {
#[cfg(unix)]
{
if let Ok(dir) = std::fs::OpenOptions::new().read(true).open(parent) {
if let Err(e) = dir.sync_all() {
eprintln!("[atomic] parent dir sync_all failed: {}", e);
}
}
}
}
Ok(())
}
fn tmp_path_for(path: &Path) -> PathBuf {
let mut p = path.to_path_buf();
let file_name = path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("tmpfile");
let tmp_name = format!("{}.tmp", file_name);
p.set_file_name(tmp_name);
p
}
pub fn sha256_bytes_local(bytes: &[u8]) -> String {
let digest = sha256_bytes(bytes);
digest_to_hex_lower(&digest)
.unwrap_or_else(|| digest.iter().map(|b| format!("{:02x}", b)).collect())
}
pub fn generate_id() -> String {
let n = ID_COUNTER.fetch_add(1, Ordering::SeqCst);
let s = format!("{}-{}", current_time_rfc3339(), n);
sha256_bytes_local(s.as_bytes())
}
pub fn checksum_file(path: &Path) -> io::Result<String> {
let data = fs::read(path)?;
let digest = sha256_bytes(&data);
Ok(digest_to_hex_lower(&digest)
.unwrap_or_else(|| digest.iter().map(|b| format!("{:02x}", b)).collect()))
}
pub fn atomic_append(path: &Path, bytes: &[u8]) -> io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let tmp_path = tmp_path_for(path);
let mut existing: Vec<u8> = Vec::new();
if path.exists() {
existing = fs::read(path)?;
}
let mut combined = existing;
if !combined.is_empty() && !combined.ends_with(b"\n") {
combined.push(b'\n');
}
combined.extend_from_slice(bytes);
if !combined.ends_with(b"\n") {
combined.push(b'\n');
}
let mut options = OpenOptions::new();
options.create(true).write(true).truncate(true);
let mut file = options.open(&tmp_path)?;
file.write_all(&combined)?;
file.flush()?;
file.sync_all()?;
fs::rename(&tmp_path, path)?;
if let Some(parent) = path.parent() {
#[cfg(unix)]
{
if let Ok(dir) = std::fs::OpenOptions::new().read(true).open(parent) {
if let Err(e) = dir.sync_all() {
eprintln!("[atomic] parent dir sync_all failed: {}", e);
}
}
}
}
Ok(())
}
}
pub mod json {
use std::collections::BTreeMap;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Object(BTreeMap<String, Value>),
Array(Vec<Value>),
String(String),
Number(f64),
Bool(bool),
Null,
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", compact_print(self))
}
}
impl FromStr for Value {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut p = Parser::new(s);
p.parse_value()
}
}
impl Value {
pub fn to_vec_pretty(&self) -> Vec<u8> {
pretty_print(self, 0).into_bytes()
}
}
fn compact_print(v: &Value) -> String {
match v {
Value::Null => "null".to_string(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => {
if n.fract() == 0.0 {
format!("{}", *n as i64)
} else {
n.to_string()
}
}
Value::String(s) => format!("\"{}\"", escape_string(s)),
Value::Array(a) => {
let items: Vec<String> = a.iter().map(compact_print).collect();
format!("[{}]", items.join(","))
}
Value::Object(o) => {
let items: Vec<String> = o
.iter()
.map(|(k, v)| format!("\"{}\":{}", escape_string(k), compact_print(v)))
.collect();
format!("{{{}}}", items.join(","))
}
}
}
fn pretty_print(v: &Value, indent: usize) -> String {
let ind = " ".repeat(indent);
match v {
Value::Null => "null".to_string(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(),
Value::String(s) => format!("\"{}\"", escape_string(s)),
Value::Array(a) => {
if a.is_empty() {
return "[]".to_string();
}
let mut s = String::from("[\n");
for (i, item) in a.iter().enumerate() {
s.push_str(&format!("{} {}", ind, pretty_print(item, indent + 1)));
if i + 1 != a.len() {
s.push(',');
}
s.push('\n');
}
s.push_str(&format!("{}]", ind));
s
}
Value::Object(o) => {
if o.is_empty() {
return "{}".to_string();
}
let mut s = String::from("{\n");
let len = o.len();
for (i, (k, v)) in o.iter().enumerate() {
s.push_str(&format!(
"{} \"{}\": {}",
ind,
escape_string(k),
pretty_print(v, indent + 1)
));
if i + 1 != len {
s.push(',');
}
s.push('\n');
}
s.push_str(&format!("{}{}", ind, "}"));
s
}
}
}
fn escape_string(s: &str) -> String {
s.chars()
.flat_map(|c| match c {
'\\' => "\\\\".chars().collect::<Vec<char>>(),
'"' => "\\\"".chars().collect::<Vec<char>>(),
'\n' => "\\n".chars().collect::<Vec<char>>(),
'\r' => "\\r".chars().collect::<Vec<char>>(),
'\t' => "\\t".chars().collect::<Vec<char>>(),
c if c.is_control() => format!("\\u{:04x}", c as u32)
.chars()
.collect::<Vec<char>>(),
c => vec![c],
})
.collect()
}
struct Parser<'a> {
s: &'a [u8],
i: usize,
}
impl<'a> Parser<'a> {
fn new(src: &'a str) -> Self {
Self {
s: src.as_bytes(),
i: 0,
}
}
fn parse_value(&mut self) -> Result<Value, String> {
self.skip_ws();
if self.eof() {
return Err("unexpected eof".into());
}
match self.peek() {
b'n' => self.parse_null(),
b't' | b'f' => self.parse_bool(),
b'\"' => self.parse_string().map(Value::String),
b'[' => self.parse_array(),
b'{' => self.parse_object(),
b'-' | b'0'..=b'9' => self.parse_number(),
_ => Err(format!("unexpected char: {}", self.peek() as char)),
}
}
fn parse_null(&mut self) -> Result<Value, String> {
self.expect_bytes(b"null")?;
Ok(Value::Null)
}
fn parse_bool(&mut self) -> Result<Value, String> {
if self.match_bytes(b"true") {
Ok(Value::Bool(true))
} else if self.match_bytes(b"false") {
Ok(Value::Bool(false))
} else {
Err("invalid boolean".into())
}
}
fn parse_number(&mut self) -> Result<Value, String> {
let start = self.i;
if self.peek() == b'-' {
self.i += 1;
}
while !self.eof() && (self.peek().is_ascii_digit()) {
self.i += 1;
}
if !self.eof() && self.peek() == b'.' {
self.i += 1;
while !self.eof() && self.peek().is_ascii_digit() {
self.i += 1;
}
}
if !self.eof() && (self.peek() == b'e' || self.peek() == b'E') {
self.i += 1;
if !self.eof() && (self.peek() == b'+' || self.peek() == b'-') {
self.i += 1;
}
while !self.eof() && self.peek().is_ascii_digit() {
self.i += 1;
}
}
let num = std::str::from_utf8(&self.s[start..self.i])
.map_err(|e| e.to_string())?
.parse::<f64>()
.map_err(|e| e.to_string())?;
Ok(Value::Number(num))
}
fn parse_string(&mut self) -> Result<String, String> {
assert_eq!(self.peek(), b'\"');
self.i += 1;
let mut out = String::new();
while !self.eof() {
let c = self.next_char()?;
if c == '\"' {
return Ok(out);
}
if c == '\\' {
let esc = self.next_char()?;
match esc {
'"' => out.push('"'),
'\\' => out.push('\\'),
'/' => out.push('/'),
'b' => out.push('\x08'),
'f' => out.push('\x0c'),
'n' => out.push('\n'),
'r' => out.push('\r'),
't' => out.push('\t'),
'u' => {
let mut code = 0u32;
for _ in 0..4 {
let ch = self.next_char()? as u32;
code = code << 4 | hexval(ch)?;
}
if let Some(ch) = std::char::from_u32(code) {
out.push(ch);
}
}
_ => return Err("invalid escape".into()),
}
} else {
out.push(c);
}
}
Err("unterminated string".into())
}
fn parse_array(&mut self) -> Result<Value, String> {
assert_eq!(self.peek(), b'[');
self.i += 1;
self.skip_ws();
let mut v = Vec::new();
if self.peek() == b']' {
self.i += 1;
return Ok(Value::Array(v));
}
loop {
let item = self.parse_value()?;
v.push(item);
self.skip_ws();
if self.peek() == b']' {
self.i += 1;
break;
}
self.expect_byte(b',')?;
self.skip_ws();
}
Ok(Value::Array(v))
}
fn parse_object(&mut self) -> Result<Value, String> {
assert_eq!(self.peek(), b'{');
self.i += 1;
self.skip_ws();
let mut m = BTreeMap::new();
if self.peek() == b'}' {
self.i += 1;
return Ok(Value::Object(m));
}
loop {
self.skip_ws();
let key = self.parse_string()?;
self.skip_ws();
self.expect_byte(b':')?;
self.skip_ws();
let val = self.parse_value()?;
m.insert(key, val);
self.skip_ws();
if self.peek() == b'}' {
self.i += 1;
break;
}
self.expect_byte(b',')?;
self.skip_ws();
}
Ok(Value::Object(m))
}
fn skip_ws(&mut self) {
while !self.eof()
&& (self.peek() == b' '
|| self.peek() == b'\n'
|| self.peek() == b'\r'
|| self.peek() == b'\t')
{
self.i += 1;
}
}
fn expect_byte(&mut self, bch: u8) -> Result<(), String> {
if self.eof() || self.peek() != bch {
Err(format!("expected {}", bch as char))
} else {
self.i += 1;
Ok(())
}
}
fn expect_bytes(&mut self, bs: &[u8]) -> Result<(), String> {
for &b in bs {
self.expect_byte(b)?;
}
Ok(())
}
fn match_bytes(&mut self, bs: &[u8]) -> bool {
if self.s.len() - self.i >= bs.len() && &self.s[self.i..self.i + bs.len()] == bs {
self.i += bs.len();
true
} else {
false
}
}
fn peek(&self) -> u8 {
self.s[self.i]
}
fn eof(&self) -> bool {
self.i >= self.s.len()
}
fn next_char(&mut self) -> Result<char, String> {
if self.eof() {
return Err("unexpected eof".into());
}
let b = self.s[self.i];
if b < 128 {
self.i += 1;
Ok(b as char)
} else {
let s = std::str::from_utf8(&self.s[self.i..]).map_err(|e| e.to_string())?;
let mut it = s.chars();
if let Some(c) = it.next() {
self.i += c.len_utf8();
Ok(c)
} else {
Err("unexpected eof utf8".into())
}
}
}
}
fn hexval(ch: u32) -> Result<u32, String> {
match ch as u8 as char {
'0'..='9' => Ok((ch as u8 - b'0') as u32),
'a'..='f' => Ok((ch as u8 - b'a' + 10) as u32),
'A'..='F' => Ok((ch as u8 - b'A' + 10) as u32),
_ => Err("invalid hex".into()),
}
}
}