///|
/// A short-term memory, from a shell.
///
/// The library is `riantr/tesseract/src/tesseract_memory`; this is the window
/// onto it. Every argument is one command, and they run in order against one
/// shared memory — the same shape `cmd/main` uses for its statements, because
/// a memory is only interesting as a *sequence* of things done to it.
///
/// ```sh
/// moon run cmd/memory -- "put greeting hello world" "get greeting"
/// moon run cmd/memory -- --capacity 3 "put a 1" "put b 2" "put c 3" "put d 4" "keys"
/// moon run cmd/memory -- --manual-clock "put a x --ttl 1000" "advance 1000" "get a"
/// moon run cmd/memory -- --scope work "put a 1" "put b 2" "scopes"
/// moon run cmd/memory -- "put a x --meta kind=note" "entries --where kind=note"
/// moon run cmd/memory -- demo
/// ```
///
/// The commands themselves live in `riantr/tesseract/src/tesseract_memory_cli`,
/// which `cmd/memory_db` shares. The two binaries differ in **where the
/// memory is kept**, not in what they can do to it: this one keeps it in the
/// process and forgets it when the process ends, and the other writes it to a
/// file that outlives it.
///
/// The options come before the commands and configure the store:
///
/// | option | meaning |
/// |---|---|
/// | `--capacity N` | how many entries the memory holds (default 100) |
/// | `--max-bytes N` | a budget for the stored values, in UTF-8 bytes (0: none) |
/// | `--ttl MS` | the default lifetime of a `put` (default: never) |
/// | `--evict NAME` | `lru`, `lfu`, `expiry` or `oldest` |
/// | `--by NAME` | `jaccard`, `cosine`, `containment` or `structure` |
/// | `--half-life MS` | halve the weight of an entry every MS (0: no decay) |
/// | `--manual-clock` | a clock that only `advance` moves |
/// | `--scope NAME` | the scope the commands work in (default: `default`) |
///
/// `--manual-clock` is what makes the TTL visible: nothing expires until the
/// clock is moved, so the demo shows the boundary rather than a race.
///
/// `--scope` picks the **partition** of the keyspace the commands work in.
/// The scopes of one memory share its clock, its budget and its statistics:
/// the capacity and the byte budget are the store's, so a write into one
/// scope can evict an entry from another. `scopes` lists the ones that hold
/// something, `scope ` switches to another one, and `search-all` looks
/// in all of them at once.
///
/// The **labels** say what an entry is. `put` writes them with `--meta k=v`,
/// and the searches and `entries` read them back with `--where k=v`,
/// `--where k` and `--without k`. A filter chooses the candidates, so an
/// entry the filter turns away is not looked at at all — even when it would
/// have been the better match.
///
/// The **pin** says an entry is never thrown away to make room for another
/// one. `put --pinned` writes a pinned entry, and `pin` and `unpin` change
/// the pin of an entry that is already there. The pin is absolute: a store
/// that cannot make room for a write without breaking a pin refuses the write
/// rather than breaking it.

///|
/// Everything the commands report goes to stdout; the banner and every failure
/// go to stderr. There is no export here writing a document, so the split is
/// only to keep a failure out of a pipeline that reads stdout.

///|
/// How this binary is run, for the help.
fn how_to_run() -> @tesseract_memory_cli.Usage {
  @tesseract_memory_cli.usage(
    "moon run cmd/memory -- [options]  [args]...",
    [],
    [],
  )
}

///|
fn main {
  let all = @env.args()
  let argv = if all.length() > 0 { all[1:] } else { all }
  let (options, commands) = @tesseract_memory_cli.parse_session(argv)
  if commands.length() == 0 {
    @tesseract_memory_cli.print_help(how_to_run())
    return
  }
  // A capacity of zero or less cannot be honoured, and a negative budget
  // would read as the one value that means no limit at all. The library
  // replaces a setting it cannot use, but a person who typed a flag deserves
  // to be told, not to be given a different memory than they asked for.
  match options.problem() {
    Some(message) => {
      @tesseract_memory_cli.warn(message)
      return
    }
    None => ()
  }
  // This binary keeps its memory in the process. A file is the other
  // binary's business, and it is a business that needs a different build:
  // the file is a `.db`, and the `.db` adapter does not build on every
  // backend this one does.
  match options.file {
    Some(_) => {
      @tesseract_memory_cli.warn(
        "this binary keeps its memory in the process; the durable one is cmd/memory_db",
      )
      return
    }
    None => ()
  }
  let config = options.config()
  let m = if options.manual {
    @tesseract_memory.new_memory_with_clock(
      config,
      @tesseract_memory.memory_clock_manual(0L),
    )
  } else {
    @tesseract_memory.new_memory_with(config)
  }
  let mut scope = m.scope(options.scope_name)
  let mut k = 0
  while k < commands.length() {
    scope = @tesseract_memory_cli.run_command(
      m,
      scope,
      commands[k],
      how_to_run(),
    )
    k = k + 1
  }
}