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