1pub mod channel;
38pub mod error;
39pub mod forgiving;
40pub mod interval;
41pub mod normalize;
42pub mod pipeline;
43pub mod plugin;
44pub mod size;
45
46use std::{fmt, str::FromStr};
47
48use serde::{Deserialize, Deserializer, Serialize};
49use serde_json::{Map, Value};
50
51pub use crate::{
52 channel::{ChannelId, ChannelTarget, HostBuilder},
53 error::{PluginError, Result},
54 forgiving::Forgiving,
55 interval::{Interval, ParseIntervalError},
56 normalize::{canonical, normalize},
57 pipeline::{Chain, Emitted, Pipeline, Registry, Segment},
58 plugin::{
59 BuildCtx, Ctx, EffectSink, Emission, Emit, Execution, ExternalStage, LogLevel,
60 PipelineMeta, Plugin, PluginFactory, Stage, StageInfo, StderrMode,
61 },
62 size::{ByteSize, ParseSizeError},
63};
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
67#[serde(rename_all = "kebab-case")]
68pub enum Direction {
69 #[serde(alias = "forward", alias = "src-to-sink", alias = "source-to-sink")]
71 SourceToSink,
72 #[serde(alias = "reverse", alias = "sink-to-src", alias = "sink-to-source")]
74 SinkToSource,
75}
76
77impl Direction {
78 pub const ALL: [Direction; 2] = [Direction::SourceToSink, Direction::SinkToSource];
79
80 #[must_use]
81 pub fn flip(self) -> Self {
82 match self {
83 Direction::SourceToSink => Direction::SinkToSource,
84 Direction::SinkToSource => Direction::SourceToSink,
85 }
86 }
87
88 #[must_use]
89 pub fn as_str(self) -> &'static str {
90 match self {
91 Direction::SourceToSink => "source-to-sink",
92 Direction::SinkToSource => "sink-to-source",
93 }
94 }
95}
96
97impl fmt::Display for Direction {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.write_str(self.as_str())
100 }
101}
102
103#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize)]
108#[serde(rename_all = "kebab-case")]
109pub enum DirectionSpec {
110 SourceToSink,
111 SinkToSource,
112 #[default]
113 Both,
114}
115
116impl<'de> Deserialize<'de> for DirectionSpec {
119 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
120 let raw = String::deserialize(deserializer)?;
121 raw.parse().map_err(serde::de::Error::custom)
122 }
123}
124
125impl DirectionSpec {
126 #[must_use]
127 pub fn contains(self, direction: Direction) -> bool {
128 matches!(
129 (self, direction),
130 (DirectionSpec::Both, _)
131 | (DirectionSpec::SourceToSink, Direction::SourceToSink)
132 | (DirectionSpec::SinkToSource, Direction::SinkToSource)
133 )
134 }
135
136 #[must_use]
137 pub fn as_str(self) -> &'static str {
138 match self {
139 DirectionSpec::SourceToSink => "source-to-sink",
140 DirectionSpec::SinkToSource => "sink-to-source",
141 DirectionSpec::Both => "both",
142 }
143 }
144}
145
146impl fmt::Display for DirectionSpec {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 f.write_str(self.as_str())
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct ParseDirectionError(pub String);
154
155impl fmt::Display for ParseDirectionError {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(
158 f,
159 "unknown direction {:?}; expected one of: source-to-sink, sink-to-source, both",
160 self.0
161 )
162 }
163}
164
165impl std::error::Error for ParseDirectionError {}
166
167impl FromStr for DirectionSpec {
168 type Err = ParseDirectionError;
169
170 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
171 match normalize(s.trim()).as_str() {
172 "sourcetosink" | "srctosink" | "forward" | "fwd" | "source" | "src" | "out" => {
173 Ok(DirectionSpec::SourceToSink)
174 }
175 "sinktosource" | "sinktosrc" | "reverse" | "rev" | "sink" | "in" => {
176 Ok(DirectionSpec::SinkToSource)
177 }
178 "both" | "bidi" | "bidirectional" | "duplex" | "all" => Ok(DirectionSpec::Both),
179 _ => Err(ParseDirectionError(s.to_string())),
180 }
181 }
182}
183
184#[derive(Debug, Clone, Default, Deserialize, Serialize)]
197pub struct PluginSpec {
198 #[serde(alias = "plugin", alias = "use")]
199 pub name: String,
200 #[serde(default)]
201 pub direction: DirectionSpec,
202 #[serde(default, rename = "as")]
206 pub alias: Option<String>,
207 #[serde(default)]
210 pub detach: Option<bool>,
211 #[serde(flatten, default)]
213 pub config: Map<String, Value>,
214}
215
216impl PluginSpec {
217 pub fn new(name: impl Into<String>, direction: DirectionSpec) -> Self {
218 Self {
219 name: name.into(),
220 direction,
221 alias: None,
222 detach: None,
223 config: Map::new(),
224 }
225 }
226
227 #[must_use]
228 pub fn named(mut self, alias: impl Into<String>) -> Self {
229 self.alias = Some(alias.into());
230 self
231 }
232
233 #[must_use]
234 pub fn detached(mut self, detach: bool) -> Self {
235 self.detach = Some(detach);
236 self
237 }
238
239 #[must_use]
240 pub fn with(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
241 self.config.insert(key.into(), value.into());
242 self
243 }
244
245 pub fn set(&mut self, key: impl Into<String>, value: impl Into<Value>) -> &mut Self {
246 self.config.insert(key.into(), value.into());
247 self
248 }
249}
250
251impl fmt::Display for PluginSpec {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 match &self.alias {
254 Some(alias) => write!(f, "{} ({}:{})", alias, self.name, self.direction),
255 None => write!(f, "{}:{}", self.name, self.direction),
256 }
257 }
258}