Skip to main content

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}