///|
/// MoonProbe - cli
///
/// Reading a `.probe` suite from disk.
///
/// The MoonBit core library ships no file system API, so the `js` backend — the
/// one that runs on Node.js — reaches the host through `fs.readFileSync`. Every
/// other backend reports that reading is unsupported rather than failing with an
/// obscure error.
#cfg(target="js")
extern "js" fn host_read_file(path : String) -> String =
  #| ((path) => {
  #|   const fs = require("fs");
  #|   try {
  #|     return "OK\n" + fs.readFileSync(path, "utf8");
  #|   } catch (e) {
  #|     return "ERR\n" + String((e && e.message) || e);
  #|   }
  #| })

///|
/// Read the `.probe` suite at `path`.
///
/// The reply from the host is `OK\n` on success and `ERR\n`
/// when the file cannot be read, so that a missing file becomes an ordinary
/// `Err` instead of a host exception crossing the FFI boundary.
#cfg(target="js")
fn read_source(path : String) -> Result[String, String] {
  let raw = host_read_file(path)
  match raw.split_once("\n") {
    Some((tag, body)) =>
      if tag == "OK" {
        Ok(body.to_owned())
      } else {
        Err("cannot read \"\{path}\": \{body}")
      }
    None => Err("cannot read \"\{path}\": malformed reply from the host")
  }
}

///|
/// Reading a suite needs a file system, which only the `js` target can reach
/// through Node.js.
#cfg(not(target="js"))
fn read_source(path : String) -> Result[String, String] {
  ignore(path)
  return Err(
    "reading .probe files needs the `js` target (Node.js): build with --target js",
  )
}

///|
#cfg(target="js")
extern "js" fn host_write_file(path : String, contents : String) -> String =
  #| ((path, contents) => {
  #|   const fs = require("fs");
  #|   try {
  #|     fs.writeFileSync(path, contents, "utf8");
  #|     return "OK\n";
  #|   } catch (e) {
  #|     return "ERR\n" + String((e && e.message) || e);
  #|   }
  #| })

///|
/// Write `contents` to the file at `path`.
///
/// Uses the same `OK\n` / `ERR\n` reply convention as `read_source`, so a write
/// failure arrives as an ordinary `Err`.
#cfg(target="js")
fn write_report(path : String, contents : String) -> Result[Unit, String] {
  let raw = host_write_file(path, contents)
  match raw.split_once("\n") {
    Some((tag, body)) =>
      if tag == "OK" {
        Ok(())
      } else {
        Err("cannot write \"\{path}\": \{body}")
      }
    None => Err("cannot write \"\{path}\": malformed reply from the host")
  }
}

///|
/// Writing a report needs a file system, which only the `js` target can reach
/// through Node.js.
#cfg(not(target="js"))
fn write_report(path : String, contents : String) -> Result[Unit, String] {
  ignore((path, contents))
  return Err(
    "writing reports needs the `js` target (Node.js): build with --target js",
  )
}