Skip to main content

asimov_patterns/programs/
fetcher.rs

1// This is free and unencumbered software released into the public domain.
2
3//! URL-to-RDF retrieval: the fetcher marker trait and options.
4
5#![allow(unused)]
6
7use crate::Execute;
8use alloc::{string::String, vec::Vec};
9use bon::Builder;
10
11const HELP_OUTPUT: &str = r#"The output format."#;
12const HELP_JQ: &str = r#"Filter JSON output using a jq expression.
13
14For example, "select(.name)"."#;
15
16/// A URL protocol client that retrieves one resource and represents it as RDF.
17///
18/// The program defines supported URL schemes and the mapping from retrieved
19/// content to RDF. A single resource may produce many RDF statements. Returning
20/// arbitrary response bytes alone does not fulfill the RDF output contract.
21///
22/// # Command-line contract
23///
24/// `PROGRAM [OPTIONS] INPUT-URL`
25///
26/// One absolute URL is required as a single argument; it is not a stdin
27/// payload or an implicitly converted local pathname. RDF is written to stdout
28/// using [`FetcherOptions::output`] (`jsonl` by default). The program validates
29/// the URL and rejects unsupported schemes.
30///
31/// `T` is the implementation's result representation. Retrieval, redirects,
32/// authentication, caching, and resource-to-RDF mapping belong to the program.
33/// See [`crate::programs`] for links to concrete execution behavior and the
34/// [fetcher specification][spec].
35///
36/// [spec]: https://asimov-specs.github.io/program-patterns/#fetcher
37pub trait Fetcher<T>: Execute<T> {}
38
39/// Output-format selection, filtering, and additional arguments for a [`Fetcher`].
40///
41/// The URL is supplied separately by the implementation's invocation API.
42/// `Default` leaves optional fields unset and `other` empty; format support and
43/// URL validity are not checked by this configuration type. A host can flatten
44/// [`crate::CachingOptions`] and [`crate::TimingOptions`] alongside this type
45/// for shared caching and execution timing arguments.
46///
47/// # Examples
48///
49/// ```rust
50/// use asimov_patterns::FetcherOptions;
51///
52/// let options = FetcherOptions::builder()
53///     .output("jsonl")
54///     .build();
55/// ```
56#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Builder)]
57#[builder(derive(Debug), on(String, into))]
58#[cfg_attr(feature = "clap", derive(clap::Args))]
59#[cfg_attr(feature = "clap", command(about = None, long_about = None))]
60pub struct FetcherOptions {
61    /// Additional arguments placed after generated options and before the URL.
62    ///
63    /// Each string is one literal argument, without shell expansion. The runner
64    /// supplies the URL separately; do not duplicate it here. See [`crate::programs`].
65    #[builder(field)]
66    #[cfg_attr(feature = "clap", clap(skip))]
67    pub other: Vec<String>,
68
69    /// The output format.
70    ///
71    /// RDF serialization passed as `--output=FORMAT` (`-o` in the CLI).
72    ///
73    /// `None` omits the option; the specified program default is `jsonl`.
74    /// This names a serialization, not a file or a raw-response retrieval mode.
75    #[cfg_attr(
76        feature = "clap",
77        clap(
78            value_name = "FORMAT",
79            short = 'o',
80            long,
81            help = HELP_OUTPUT,
82            long_help = HELP_OUTPUT
83        )
84    )]
85    pub output: Option<String>,
86
87    /// Filter JSON output using a jq expression.
88    ///
89    /// Applied by the host to retrieved output, not forwarded to the external
90    /// fetcher by `asimov-runner`. This type stores the expression without
91    /// compiling or evaluating it.
92    #[cfg_attr(
93        feature = "clap",
94        clap(
95            long,
96            value_name = "EXPR",
97            help = "Filter JSON output using a jq expression",
98            long_help = HELP_JQ
99        )
100    )]
101    pub jq: Option<String>,
102}
103
104impl<S: fetcher_options_builder::State> FetcherOptionsBuilder<S> {
105    /// Appends one literal argument to [`FetcherOptions::other`], preserving order.
106    pub fn other(mut self, flag: impl Into<String>) -> Self {
107        self.other.push(flag.into());
108        self
109    }
110
111    /// Appends a present argument to [`FetcherOptions::other`]; `None` adds nothing.
112    pub fn maybe_other(mut self, flag: Option<impl Into<String>>) -> Self {
113        if let Some(flag) = flag {
114            self.other.push(flag.into());
115        }
116        self
117    }
118}