harn-stdlib 0.8.10

Embedded Harn standard library source catalog
Documentation
// std/settled — aggregate `parallel settle` Result lists into typed shapes.
//
// `parallel settle xs with { max_concurrent: N } { x -> ... }` returns
// `{results: list<Result>, ...}`. Harness authors invariably re-derive the
// same shapes from those results: ok-only values, error-only messages,
// counts, and tallies. These helpers replace ~15-30 LOC of bookkeeping
// per parallel block.
//
// Import with: import { partition, oks, errs, count_oks, ... } from "std/settled"
/** Counts and split lists from a settled-results aggregation. */
type SettledPartition = {oks: list, errs: list, ok_count: int, err_count: int}

/**
 * Split a list of Result values into successful values and error payloads.
 * Returns `{oks, errs, ok_count, err_count}` so callers can count and
 * iterate without re-walking the list.
 */
pub fn partition(results: list) -> SettledPartition {
  var oks = []
  var errs = []
  for r in results {
    if is_ok(r) {
      oks = oks + [unwrap(r)]
    } else {
      errs = errs + [unwrap_err(r)]
    }
  }
  return {oks: oks, errs: errs, ok_count: len(oks), err_count: len(errs)}
}

/** Return only the successful values (drops errors). */
pub fn oks(results: list) -> list {
  var out = []
  for r in results {
    if is_ok(r) {
      out = out + [unwrap(r)]
    }
  }
  return out
}

/** Return only the error payloads (drops successes). */
pub fn errs(results: list) -> list {
  var out = []
  for r in results {
    if is_err(r) {
      out = out + [unwrap_err(r)]
    }
  }
  return out
}

/** Count successful Results in the list. */
pub fn count_oks(results: list) -> int {
  var n = 0
  for r in results {
    if is_ok(r) {
      n = n + 1
    }
  }
  return n
}

/** Count error Results in the list. */
pub fn count_errs(results: list) -> int {
  var n = 0
  for r in results {
    if is_err(r) {
      n = n + 1
    }
  }
  return n
}

/**
 * Map successes through `on_ok` and errors through `on_err`, collecting the
 * unified output list. The closures receive `(value, index)` for successes
 * and `(error, index)` for errors so callers can preserve the original
 * argument's position when reporting.
 */
pub fn map_settled(results: list, on_ok, on_err) -> list {
  var out = []
  var i = 0
  for r in results {
    if is_ok(r) {
      out = out + [on_ok(unwrap(r), i)]
    } else {
      out = out + [on_err(unwrap_err(r), i)]
    }
    i = i + 1
  }
  return out
}

/**
 * Group results by a string label produced by `classifier`. Errors are
 * passed through `classifier` after being unwrapped; if `classifier` is
 * nil, errors fall under the "error" bucket and successes under "ok".
 */
pub fn tally_by(results: list, classifier) -> dict<string, int> {
  var counts = {}
  for r in results {
    let label = if classifier == nil {
      if is_ok(r) {
        "ok"
      } else {
        "error"
      }
    } else {
      let payload = if is_ok(r) {
        unwrap(r)
      } else {
        unwrap_err(r)
      }
      to_string(classifier(payload, is_ok(r)))
    }
    let prior = counts[label] ?? 0
    counts = counts + {[label]: prior + 1}
  }
  return counts
}

/**
 * Iterate Results as `{ok, value, index}` records. Useful when callers want
 * one consolidated loop with both branches in scope at the same point.
 */
pub fn enumerate_settled(results: list) -> list<dict> {
  var out = []
  var i = 0
  for r in results {
    let row = if is_ok(r) {
      {ok: true, value: unwrap(r), error: nil, index: i}
    } else {
      {ok: false, value: nil, error: unwrap_err(r), index: i}
    }
    out = out + [row]
    i = i + 1
  }
  return out
}

/**
 * Convert a settled-results list into `{any_failed, all_ok, ok_count,
 * err_count, total}` so harnesses can produce one-shot summary output
 * without re-walking the list.
 */
pub fn summary(results: list) -> dict {
  let counts = partition(results)
  return {
    any_failed: counts.err_count > 0,
    all_ok: counts.err_count == 0 && counts.ok_count > 0,
    ok_count: counts.ok_count,
    err_count: counts.err_count,
    total: counts.ok_count + counts.err_count,
  }
}