asimov_runner/programs/reader.rs
1// This is free and unencumbered software released into the public domain.
2
3//! RDF dataset import through an external reader program.
4
5use crate::{AnyInput, CommandExt, Executor, ExecutorError, GraphOutput, JsonlStream};
6use alloc::boxed::Box;
7use async_trait::async_trait;
8use derive_more::Debug;
9use std::{ffi::OsStr, process::Stdio};
10
11pub use asimov_patterns::ReaderOptions;
12
13/// A live JSONL graph stream, or an error starting the reader.
14pub type ReaderResult = Result<JsonlStream, ExecutorError>;
15
16/// An external [reader] that imports input data into an RDF dataset.
17///
18/// This is a program-pattern wrapper, not an implementation of an I/O reader
19/// trait. Input parsing and conversion are performed by the external program.
20/// Execution uses the concurrent streaming behavior described in
21/// [`crate::programs`].
22///
23/// [reader]: https://asimov-specs.github.io/program-patterns/#reader
24#[allow(unused)]
25#[derive(Debug)]
26pub struct Reader {
27 executor: Executor,
28 options: ReaderOptions,
29 input: AnyInput,
30 output: GraphOutput,
31}
32
33impl Reader {
34 /// Configures a reader without starting it.
35 ///
36 /// Adds any configured `--input=<format>` and `--output=<format>` arguments,
37 /// followed by `options.other`. The input and output values select stdin
38 /// and stdout; stderr is captured for failure diagnostics.
39 pub fn new(
40 program: impl AsRef<OsStr>,
41 input: AnyInput,
42 output: GraphOutput,
43 options: ReaderOptions,
44 ) -> Self {
45 let mut executor = Executor::new(program);
46 executor
47 .command()
48 .option("input", options.input.as_ref())
49 .option("output", options.output.as_ref())
50 .args(&options.other)
51 .stdin(input.as_stdio())
52 .stdout(output.as_stdio())
53 .stderr(Stdio::piped());
54
55 Self {
56 executor,
57 options,
58 input,
59 output,
60 }
61 }
62
63 /// Starts a child and returns its live JSONL graph stream.
64 ///
65 /// After successful spawning, input ownership moves into the stream, which
66 /// feeds it concurrently when polled. Subsequent executions have no source input.
67 ///
68 /// # Errors
69 ///
70 /// Spawn failures are returned directly; input, output, wait, and exit failures are
71 /// stream items. Consume the stream to completion to check process success.
72 pub async fn execute(&mut self) -> ReaderResult {
73 self.executor
74 .execute_jsonl_with_io(&mut self.input, &mut self.output)
75 .await
76 }
77}
78
79impl asimov_patterns::Reader<JsonlStream> for Reader {}
80
81crate::batch::with_batching!(Reader);
82
83crate::pipeline::stage!(
84 Reader,
85 value,
86 value.input,
87 value.output,
88 None,
89 value.options.output.as_deref()
90);
91
92#[async_trait]
93impl asimov_patterns::Execute<JsonlStream> for Reader {
94 type Error = ExecutorError;
95
96 async fn execute(&mut self) -> ReaderResult {
97 self.execute().await
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 //use super::*;
104 //use asimov_patterns::Execute;
105
106 #[tokio::test]
107 async fn test_execute() {
108 // TODO
109 }
110}