harn-stdlib 0.8.63

Embedded Harn standard library source catalog
Documentation
// std/rules - run declarative structural rules (the harn-rules engine) from
// Harn. A rule is its TOML source, so an agent can author and run a rule
// inline without recompiling the binary.
/**
 * Run a rule and return its matches.
 *
 * `params` is a dict:
 *
 * - `rule`: the rule's TOML source (id / language / `[rule]` block / `where`
 *   / `transform` / `fix` / `safety`).
 * - `source` **or** `paths`: inline text (with `language`) or a list of file
 *   paths to read.
 *
 * On success `result == "ok"` and `matches` is a list of `{path, text,
 * start_row, start_col, captures}` where `captures` maps each metavar name
 * to its bound text.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: [backend]
 * @api_stability: experimental
 * @example: rules_search({rule: src, source: "foo();", language: "typescript"})
 */
pub fn rules_search(params) -> dict {
  let payload = hostlib_rules_search(params ?? {}) ?? {}
  return payload
    .merge(
    {ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_search"}},
  )
}

/**
 * Run a rule in **report-only** mode and return a data table — columnar
 * findings (`rows`) plus a metrics `summary` (total / files / per-file
 * counts). Never edits anything.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: [backend]
 * @api_stability: experimental
 * @example: rules_report({rule: src, paths: ["a.ts", "b.ts"]})
 */
pub fn rules_report(params) -> dict {
  let payload = hostlib_rules_report(params ?? {}) ?? {}
  return payload.merge({provenance: {module: "std/rules", helper: "rules_report"}})
}

/**
 * Run a declarative rule and return its **diagnostics** — the lint surface of
 * a rule that carries a `message`. On success `result == "ok"` and
 * `diagnostics` is a list of `{path, rule_id, message, severity, start_row,
 * start_col, end_row, end_col, applicability, fix}` (one per match; `fix` is
 * `nil` unless the rule has a `fix` template).
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: [backend]
 * @api_stability: experimental
 * @example: rules_diagnostics({rule: src, source: "foo();", language: "typescript"})
 */
pub fn rules_diagnostics(params) -> dict {
  let payload = hostlib_rules_diagnostics(params ?? {}) ?? {}
  return payload
    .merge(
    {ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_diagnostics"}},
  )
}

/**
 * Run a rule's matcher, then call an **imperative** visitor `on_match(node,
 * ctx)` once per match — the escape hatch for logic a declarative rule cannot
 * express (compute a message or fix from the captured metavars, filter on
 * arbitrary conditions). `params` is `{rule, source|paths, language?,
 * on_match}` where `on_match` is `fn(node, ctx)`:
 *
 * - `node`: `{text, captures, start_row, start_col, end_row, end_col}` —
 *   `captures` maps each metavar name to its bound text.
 * - `ctx`: `{path, language, source, rule_id}` (read-only).
 * - returns: `nil`/`false` to skip the match, `true` to flag it with the
 *   rule's defaults, a `{message, fix, safety, severity}` dict, or a list of
 *   such dicts (to report several findings on one node).
 *
 * Read-only — `visit` never writes; apply fixes with `rules_apply` or your own
 * write. On success `result == "ok"` and `diagnostics` is a list in the same
 * shape `rules_diagnostics` emits, so an imperative rule and its declarative
 * equivalent produce identical diagnostics.
 *
 * @effects: [host]
 * @allocation: heap
 * @errors: [backend]
 * @api_stability: experimental
 * @example: rules_visit({rule: src, source: "f();", language: "typescript", on_match: fn(node, ctx) { return {message: "call"} }})
 */
pub fn rules_visit(params) -> dict {
  let payload = hostlib_rules_visit(params ?? {}) ?? {}
  return payload
    .merge(
    {ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_visit"}},
  )
}

/**
 * Apply a codemod rule's `fix`. **Dry-run by default** — pass `dry_run:
 * false` to write. Writes only when the rule's `safety` is machine-applicable
 * (`format-only` / `behavior-preserving`) or `allow_unsafe: true`. Each file
 * entry reports `{path, changed, applied, idempotent, safety, preview}`.
 *
 * Requires the deterministic-tools gate
 * (`hostlib_enable("tools:deterministic")`).
 *
 * @effects: [host, fs]
 * @allocation: heap
 * @errors: [backend]
 * @api_stability: experimental
 * @example: rules_apply({rule: src, paths: ["a.ts"], dry_run: false})
 */
pub fn rules_apply(params) -> dict {
  let payload = hostlib_rules_apply(params ?? {}) ?? {}
  return payload
    .merge(
    {ok: payload?.result ?? "" == "ok", provenance: {module: "std/rules", helper: "rules_apply"}},
  )
}