asimov_patterns/programs/resolver.rs
1// This is free and unencumbered software released into the public domain.
2
3//! URI resolution: the resolver marker trait and URL-result limits.
4
5use crate::Execute;
6use alloc::{string::String, vec::Vec};
7use bon::Builder;
8
9/// A URI resolver that identifies zero or more resource locations as URLs.
10///
11/// Input can be a URN or URL. Resolution identifies locations; it does not
12/// retrieve the resources at those locations. The program defines supported
13/// schemes, result order, and duplicate handling. No matches is a valid result.
14///
15/// # Command-line contract
16///
17/// `PROGRAM [OPTIONS] INPUT-URI`
18///
19/// One absolute URI is required as a single argument; omission does not select
20/// stdin. [`ResolverOptions::limit`] bounds the number of URLs. The program
21/// writes zero or more UTF-8 absolute URLs to stdout, one per LF-terminated
22/// line, with no blank records, header, or JSON envelope. Zero results means
23/// zero output bytes.
24///
25/// Hosts parsing these records preserve order and accept CRLF and a final
26/// nonempty line without a terminator. Removing line endings does not authorize
27/// trimming other whitespace, percent-decoding, or splitting on commas/spaces.
28///
29/// `T` is the implementation's result representation, not necessarily one URL.
30/// See [`crate::programs`] for links to concrete execution and parsing behavior,
31/// and the [resolver specification][spec].
32///
33/// [spec]: https://asimov-specs.github.io/program-patterns/#resolver
34pub trait Resolver<T>: Execute<T> {}
35
36/// Result-count selection and additional arguments for a [`Resolver`].
37///
38/// The URI is supplied separately by the runner. `Default` leaves `limit`
39/// unset and `other` empty, imposing no caller-requested limit. The standard
40/// output framing is fixed; this type has no output-format field.
41///
42/// # Examples
43///
44/// ```rust
45/// use asimov_patterns::ResolverOptions;
46///
47/// let options = ResolverOptions::builder()
48/// .limit(100)
49/// .build();
50/// ```
51#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Builder)]
52#[builder(derive(Debug), on(String, into))]
53pub struct ResolverOptions {
54 /// Additional arguments placed after the generated limit and before the URI.
55 ///
56 /// Each string is one literal argument, without shell expansion. The runner
57 /// supplies the URI separately; do not duplicate it here. See [`crate::programs`].
58 #[builder(field)]
59 pub other: Vec<String>,
60
61 /// Maximum number of output URLs, passed as `--limit=COUNT` (`-n` in the CLI).
62 ///
63 /// `None` imposes no caller-requested limit; `Some(0)` requests no results.
64 /// A zero limit may still trigger validation or resource-availability checks.
65 /// The runner forwards a decimal integer without checking the program's
66 /// supported range; this is not a byte limit or a timeout.
67 pub limit: Option<usize>,
68}
69
70impl<S: resolver_options_builder::State> ResolverOptionsBuilder<S> {
71 /// Appends one literal argument to [`ResolverOptions::other`], preserving order.
72 pub fn other(mut self, flag: impl Into<String>) -> Self {
73 self.other.push(flag.into());
74 self
75 }
76
77 /// Appends a present argument to [`ResolverOptions::other`]; `None` adds nothing.
78 pub fn maybe_other(mut self, flag: Option<impl Into<String>>) -> Self {
79 if let Some(flag) = flag {
80 self.other.push(flag.into());
81 }
82 self
83 }
84}