asimov_runner/programs/
matcher.rs1use crate::{CommandExt, Executor, ExecutorError, GraphInput, 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::MatcherOptions;
12
13pub type MatcherResult = Result<JsonlStream, ExecutorError>;
15
16#[allow(unused)]
25#[derive(Debug)]
26pub struct Matcher {
27 executor: Executor,
28 options: MatcherOptions,
29 input: GraphInput,
30 output: GraphOutput,
31}
32
33impl Matcher {
34 pub fn new(
41 program: impl AsRef<OsStr>,
42 input: GraphInput,
43 output: GraphOutput,
44 options: MatcherOptions,
45 ) -> Self {
46 let mut executor = Executor::new(program);
47 executor
48 .command()
49 .option("input", options.input.as_ref())
50 .option("output", options.output.as_ref())
51 .args(&options.other)
52 .stdin(input.as_stdio())
53 .stdout(output.as_stdio())
54 .stderr(Stdio::piped());
55
56 Self {
57 executor,
58 options,
59 input: input.into_jsonl(),
60 output,
61 }
62 }
63
64 pub async fn execute(&mut self) -> MatcherResult {
74 self.executor
75 .execute_jsonl_with_io(&mut self.input, &mut self.output)
76 .await
77 }
78}
79
80impl asimov_patterns::Matcher<JsonlStream> for Matcher {}
81
82crate::batch::with_batching!(Matcher);
83
84crate::pipeline::stage!(
85 Matcher,
86 value,
87 value.input,
88 value.output,
89 value.options.input.as_deref(),
90 value.options.output.as_deref()
91);
92
93#[async_trait]
94impl asimov_patterns::Execute<JsonlStream> for Matcher {
95 type Error = ExecutorError;
96
97 async fn execute(&mut self) -> MatcherResult {
98 self.execute().await
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::StreamExt;
106 use alloc::vec::Vec;
107 use std::io::Cursor;
108
109 #[test]
110 fn test_options() {
111 let options = MatcherOptions::builder()
112 .input("jsonl")
113 .output("nquads")
114 .other("--exact")
115 .maybe_other(Some("--custom"))
116 .maybe_other(None::<&str>)
117 .build();
118 let mut matcher = Matcher::new(
119 "asimov-test-matcher",
120 GraphInput::Ignored,
121 GraphOutput::Captured,
122 options,
123 );
124 let args: Vec<_> = matcher.executor.command().as_std().get_args().collect();
125 assert_eq!(
126 args,
127 ["--input=jsonl", "--output=nquads", "--exact", "--custom"]
128 );
129 }
130
131 #[test]
132 fn test_default_options() {
133 let mut matcher = Matcher::new(
134 "asimov-test-matcher",
135 GraphInput::Ignored,
136 GraphOutput::Captured,
137 MatcherOptions::default(),
138 );
139 assert_eq!(matcher.executor.command().as_std().get_args().count(), 0);
140 }
141
142 #[cfg(unix)]
143 #[tokio::test]
144 async fn test_execute() {
145 let graph = b"{\"subject\":\"https://example.com/\"}\n";
146 let mut matcher = Matcher::new(
147 "cat",
148 GraphInput::AsyncRead(Box::new(Cursor::new(graph.to_vec()))),
149 GraphOutput::Captured,
150 MatcherOptions::default(),
151 );
152 let mut output = asimov_patterns::Execute::execute(&mut matcher)
153 .await
154 .unwrap();
155 let batch = output.next().await.unwrap().unwrap();
156 assert_eq!(batch.len(), 1);
157 assert_eq!(batch.lines().next().unwrap(), graph);
158 assert!(output.next().await.is_none());
159 }
160}