harn-stdlib 0.10.118

Embedded Harn standard library source catalog
Documentation
import { command_combined } from "std/command/output"
import { CommandResult } from "std/command/results"

pub type CommandSpec = list<string> | dict

const COMMAND_COMMON_OPTION_KEYS = [
  "cwd",
  "env",
  "env_remove",
  "env_mode",
  "stdin",
  "timeout_ms",
  "sandbox_profile",
  "background",
  "background_after_ms",
  "progress_interval_ms",
  "progress_max_inline_bytes",
  "policy_context",
]

/**
 * Normalize omitted command options to an empty dictionary.
 *
 * @effects: []
 * @errors: []
 */
pub fn __command_options(options: dict?) -> dict {
  return options ?? {}
}

fn __command_require_argv(argv: any) {
  if type_of(argv) != "list" || len(argv) == 0 {
    throw "command_run: argv must be a non-empty list"
  }
  return argv
}

fn __command_copy_common(req: dict, source: CommandSpec) {
  if source == nil {
    return req
  }
  let out = req
  for key in COMMAND_COMMON_OPTION_KEYS {
    if source?.[key] != nil {
      out = out + {[key]: source[key]}
    }
  }
  let capture = source?.capture
  if source?.max_inline_bytes != nil {
    const base_capture = capture ?? {}
    capture = base_capture + {max_inline_bytes: source.max_inline_bytes}
  }
  if capture != nil {
    out = out + {capture: capture}
  }
  return out
}

fn __command_apply_option_defaults(req: any, options: dict?) {
  const opts = __command_options(options)
  let out = req
  for key in COMMAND_COMMON_OPTION_KEYS {
    if out?.[key] == nil && opts[key] != nil {
      out = out + {[key]: opts[key]}
    }
  }
  if out.mode == "shell" {
    for key in ["shell", "shell_id", "login", "interactive"] {
      if out?.[key] == nil && opts[key] != nil {
        out = out + {[key]: opts[key]}
      }
    }
  }
  let capture = out?.capture ?? opts?.capture
  if opts?.max_inline_bytes != nil && capture?.max_inline_bytes == nil {
    const base_capture = capture ?? {}
    capture = base_capture + {max_inline_bytes: opts.max_inline_bytes}
  }
  if capture != nil {
    out = out + {capture: capture}
  }
  return out
}

fn __command_request(spec: CommandSpec, options: dict?) {
  if type_of(spec) == "list" {
    return __command_apply_option_defaults(
      {mode: "argv", argv: __command_require_argv(spec)},
      options,
    )
  }
  if type_of(spec) != "dict" {
    throw "command_run: spec must be an argv list or command spec dict"
  }
  const mode = spec?.mode ?? "argv"
  let req = {}
  if mode == "argv" {
    if spec?.command != nil && spec?.argv == nil {
      throw "command_run: shell mode requires mode: \"shell\""
    }
    req = {mode: "argv", argv: __command_require_argv(spec?.argv)}
  } else if mode == "shell" {
    if type_of(spec?.command) != "string" || trim(spec.command) == "" {
      throw "command_run: shell mode requires a non-empty command"
    }
    req = {mode: "shell", command: spec.command}
    for key in ["shell", "shell_id", "login", "interactive"] {
      if spec?.[key] != nil {
        req = req + {[key]: spec[key]}
      }
    }
  } else {
    throw "command_run: unsupported mode " + to_string(mode)
  }
  return __command_apply_option_defaults(__command_copy_common(req, spec), options)
}

fn __command_success(result: dict?) -> bool {
  if result?.success != nil {
    return result.success ? true : false
  }
  const {status = "completed", exit_code = -1} = result ?? {}
  return status == "completed" && exit_code == 0 && !(result?.timed_out ?? false)
}

/**
 * Project one host command result into the shared command-result contract.
 *
 * @effects: []
 * @errors: []
 */
pub fn __command_normalize_result(result: dict?) -> CommandResult {
  const raw = result ?? {}
  const success = __command_success(raw)
  const status = if raw?.status != nil {
    raw.status
  } else if success {
    "completed"
  } else {
    "failed"
  }
  return raw
    + {
      ok: success,
      success: success,
      status: status,
      exit_code: raw?.exit_code,
      cwd: raw?.cwd ?? "",
      timed_out: raw?.timed_out ?? (raw?.status == "timed_out"),
      command_id: raw?.command_id,
      handle_id: raw?.handle_id,
      stdout: raw?.stdout ?? "",
      stderr: raw?.stderr ?? "",
      combined: command_combined(raw),
      output_path: raw?.output_path,
      stdout_path: raw?.stdout_path,
      stderr_path: raw?.stderr_path,
      byte_count: raw?.byte_count,
      line_count: raw?.line_count,
      output_sha256: raw?.output_sha256,
      started_at: raw?.started_at,
      ended_at: raw?.ended_at,
      duration_ms: raw?.duration_ms,
    }
}

/**
 * Run an argv-first host command and normalize its result for `std/command`.
 *
 * @effects: [process]
 * @errors: [process_spawn]
 */
pub fn command_run_impl(
  tools: HarnessTools,
  spec: CommandSpec,
  options: dict? = nil,
) -> CommandResult {
  return __command_normalize_result(tools.run_command(__command_request(spec, options)))
}