asimov_patterns/programs/indexer.rs
1// This is free and unencumbered software released into the public domain.
2
3//! Persistent RDF indexing: the indexer marker trait and input options.
4
5use crate::Execute;
6use alloc::{string::String, vec::Vec};
7use bon::Builder;
8
9/// An RDF consumer that creates or updates a persistent index without payload output.
10///
11/// The program defines its index format, creation and update semantics,
12/// concurrency behavior, and guarantees on failure. `Ok(())` represents
13/// successful completion under those guarantees; it does not independently
14/// promise a transaction, crash durability, or safe replay after failure.
15///
16/// # Command-line contract
17///
18/// `PROGRAM [OPTIONS] [INPUT-FILE] INDEX-FILE`
19///
20/// With one operand, that operand is the index destination and input is stdin.
21/// With two, the first selects the input file (`-` for stdin) and the second
22/// selects the destination. An index destination is required and can be a file
23/// or directory according to the program. It cannot be `-`; use `./-` for a
24/// literal path of that name. Stdout carries no payload.
25///
26/// [`IndexerOptions::input`] selects the RDF format (`jsonl` by default).
27/// [`IndexerOptions::other`] can supply the destination operand. See
28/// [`crate::programs`] for links to concrete execution behavior and the
29/// [indexer specification][spec].
30///
31/// [spec]: https://asimov-specs.github.io/program-patterns/#indexer
32pub trait Indexer: Execute<()> {}
33
34/// Input-format selection and additional arguments for an [`Indexer`].
35///
36/// `Default` leaves `input` unset and `other` empty. It does **not** constitute
37/// a complete command-line invocation: the required index destination must
38/// still be supplied. The example configures the runner's stdin-input form.
39///
40/// # Examples
41///
42/// ```rust
43/// use asimov_patterns::IndexerOptions;
44///
45/// let options = IndexerOptions::builder()
46/// .input("jsonl")
47/// .other("./catalog.index")
48/// .build();
49/// ```
50#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Builder)]
51#[builder(derive(Debug), on(String, into))]
52pub struct IndexerOptions {
53 /// Additional arguments, including the required index-destination operand.
54 ///
55 /// The process wrapper appends these after `--input`. Put extension options
56 /// first, then either the index path alone or input path followed by index
57 /// path. Each string is one literal argument; see [`crate::programs`].
58 #[builder(field)]
59 pub other: Vec<String>,
60
61 /// RDF serialization passed as `--input=FORMAT` (`-i` in the CLI).
62 ///
63 /// `None` omits the option; the specified program default is `jsonl`.
64 /// This selects neither the input file nor the persistent index format.
65 pub input: Option<String>,
66}
67
68impl<S: indexer_options_builder::State> IndexerOptionsBuilder<S> {
69 /// Appends one literal argument to [`IndexerOptions::other`], preserving order.
70 pub fn other(mut self, flag: impl Into<String>) -> Self {
71 self.other.push(flag.into());
72 self
73 }
74
75 /// Appends a present argument to [`IndexerOptions::other`]; `None` adds nothing.
76 pub fn maybe_other(mut self, flag: Option<impl Into<String>>) -> Self {
77 if let Some(flag) = flag {
78 self.other.push(flag.into());
79 }
80 self
81 }
82}