use crossbeam_channel::{Receiver, unbounded};
use simd_json::OwnedValue as Value;
use std::fs;
use std::io::{self, Read};
use std::path::PathBuf;
use std::thread::{self, JoinHandle};
use crate::error::LoadError;
pub struct LoadResult {
pub result: Result<Vec<Value>, LoadError>,
}
pub struct Loader {
result_rx: Receiver<LoadResult>,
_handle: JoinHandle<()>,
}
impl Loader {
pub fn spawn_file(path: PathBuf) -> Self {
let (result_tx, result_rx) = unbounded();
let handle = thread::spawn(move || {
let result = load_file(&path);
let _ = result_tx.send(LoadResult { result });
});
Loader {
result_rx,
_handle: handle,
}
}
pub fn spawn_stdin() -> Self {
let (result_tx, result_rx) = unbounded();
let handle = thread::spawn(move || {
let result = load_stdin();
let _ = result_tx.send(LoadResult { result });
});
Loader {
result_rx,
_handle: handle,
}
}
pub fn try_recv(&self) -> Option<LoadResult> {
self.result_rx.try_recv().ok()
}
}
fn load_file(path: &PathBuf) -> Result<Vec<Value>, LoadError> {
let content = fs::read_to_string(path)?;
parse_json(&content)
}
fn load_stdin() -> Result<Vec<Value>, LoadError> {
let mut content = String::new();
io::stdin().read_to_string(&mut content)?;
if content.is_empty() {
return Err(LoadError::EmptyInput);
}
parse_json(&content)
}
fn parse_json(input: &str) -> Result<Vec<Value>, LoadError> {
let mut input_bytes = input.as_bytes().to_vec();
if let Ok(value) = simd_json::to_owned_value(&mut input_bytes) {
return Ok(vec![value]);
}
let mut values = Vec::new();
for (line_num, line) in input.lines().enumerate() {
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
let mut line_bytes = trimmed.as_bytes().to_vec();
let value =
simd_json::to_owned_value(&mut line_bytes).map_err(|e| LoadError::ParseLine {
line: line_num + 1,
message: e.to_string(),
})?;
values.push(value);
}
if values.is_empty() {
return Err(LoadError::NoValidJson);
}
Ok(values)
}