Skip to main content

asimov_runner/programs/
adapter.rs

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