///|
// Strategy: grep-execution
// Output policy is selected once. Binary detection remains a dynamic input gate.
priv enum GrepExecution {
  Summary(Bool)
  Records
  BatchedRecords
  MatchSpans
}

///|
fn GrepOptions::execution(
  self : GrepOptions,
  prefix_filename : Bool,
) -> GrepExecution {
  if self.count ||
    self.files_with_matches ||
    self.files_without_match ||
    self.quiet {
    return Summary(!self.quiet && !self.files_with_matches)
  }
  if self.only_matching && !self.invert {
    return MatchSpans
  }
  if !self.only_matching &&
    !self.context_enabled &&
    !self.line_number &&
    !self.byte_offset &&
    !prefix_filename {
    return BatchedRecords
  }
  Records
}

///|
// Complete buffered records share one accounting boundary. Cross-chunk next()
// accounts bytes as they arrive and deliberately keeps its separate error path.
#inline
fn GrepInput::consume_records(
  self : GrepInput,
  end : Int32,
  records : Int64,
) -> Unit {
  self.offset += (end - self.position).to_int64()
  self.position = end
  self.number += records
}