guartcl 0.5.0

Enhanced Jim Tcl.
Documentation
//! typedata JSON interchange

use std::{fs::File, io::Write};

use jimtcl::{
    Interp, JimError, JimObject, JimResult, check_or_error, custom::JimExtensionType, tcl_error,
};
use serde_json::{from_reader, to_string, to_string_pretty, to_writer, to_writer_pretty};

use super::TypedData;

/// Parse JSON into a Typed Data structure.
pub fn td_parse_json<'jim>(
    _interp: &'jim Interp,
    args: &[JimObject<'jim>],
) -> JimResult<TypedData> {
    let usage = "usage: td parse json ?-file? <input>";
    check_or_error!(args.len() >= 2, usage);
    if args[1].as_str()? == "-file" {
        check_or_error!(args.len() == 3, usage);
        let path = &args[2];
        path.require_untainted("path")?;
        let file = File::open(path.as_str()?)?;
        from_reader(file).map_err(JimError::wrap)
    } else {
        check_or_error!(args.len() == 2, usage);
        let data = TypedData::extract_ref(&args[1])?;
        // FIXME speed this up by not cloning
        Ok(data.clone())
    }
}

/// Save a Typed Data structure as JSON.
pub fn td_dump_json<'jim>(_interp: &'jim Interp, args: &[JimObject<'jim>]) -> JimResult<String> {
    let usage = "usage: td dump json ?-pretty? ?-file out? <td>";
    let mut pretty = false;
    let mut out_file = None;
    let mut pos = 1;
    while pos < args.len() {
        let arg = &args[pos];
        let astr = arg.as_str()?;
        if astr == "-pretty" {
            pretty = true;
            pos += 1;
        } else if astr == "-file" {
            let path = &args[pos + 1];
            path.require_untainted("path")?;
            out_file = Some(path.as_str()?);
            pos += 2;
        } else if astr == "--" {
            pos += 1;
            break;
        } else if astr.starts_with("-") {
            return Err(tcl_error!("invalid option {}", astr));
        } else {
            break;
        }
    }
    check_or_error!(pos == args.len() - 1, usage);
    let data = TypedData::extract(&args[pos])?;
    if let Some(f) = out_file {
        let mut out = File::options()
            .create(true)
            .write(true)
            .truncate(true)
            .open(f)?;
        if pretty {
            to_writer_pretty(out, &data)
                .map_err(|e| tcl_error!("could not generate JSON: {}", e))?;
        } else {
            to_writer(&mut out, &data).map_err(|e| tcl_error!("could not generate JSON: {}", e))?;
            out.write_all(b"\n")?;
        }
        Ok("".into())
    } else {
        let text = if pretty {
            to_string_pretty(&data)
        } else {
            to_string(&data)
        };
        text.map_err(|e| tcl_error!("could not generate JSON: {}", e))
    }
}