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::{BoundaryFault, Chain, Emitted, Pipeline, Registry, Segment, Side},
58 plugin::{
59 Boundaries, BuildCtx, Ctx, EffectSink, Emission, Emit, Execution, ExternalStage, LogLevel,
60 Needs, 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)]
111#[serde(rename_all = "kebab-case")]
112pub enum DirectionSpec {
113 #[default]
114 SourceToSink,
115 SinkToSource,
116 Both,
117}
118
119impl<'de> Deserialize<'de> for DirectionSpec {
122 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
123 let raw = String::deserialize(deserializer)?;
124 raw.parse().map_err(serde::de::Error::custom)
125 }
126}
127
128impl DirectionSpec {
129 #[must_use]
130 pub fn contains(self, direction: Direction) -> bool {
131 matches!(
132 (self, direction),
133 (DirectionSpec::Both, _)
134 | (DirectionSpec::SourceToSink, Direction::SourceToSink)
135 | (DirectionSpec::SinkToSource, Direction::SinkToSource)
136 )
137 }
138
139 #[must_use]
140 pub fn as_str(self) -> &'static str {
141 match self {
142 DirectionSpec::SourceToSink => "source-to-sink",
143 DirectionSpec::SinkToSource => "sink-to-source",
144 DirectionSpec::Both => "both",
145 }
146 }
147}
148
149impl fmt::Display for DirectionSpec {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f.write_str(self.as_str())
152 }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct ParseDirectionError(pub String);
157
158impl fmt::Display for ParseDirectionError {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 write!(
161 f,
162 "unknown direction {:?}; expected one of: source-to-sink, sink-to-source, both",
163 self.0
164 )
165 }
166}
167
168impl std::error::Error for ParseDirectionError {}
169
170impl FromStr for DirectionSpec {
171 type Err = ParseDirectionError;
172
173 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
174 match normalize(s.trim()).as_str() {
175 "sourcetosink" | "srctosink" | "forward" | "fwd" | "source" | "src" | "out" => {
176 Ok(DirectionSpec::SourceToSink)
177 }
178 "sinktosource" | "sinktosrc" | "reverse" | "rev" | "sink" | "in" => {
179 Ok(DirectionSpec::SinkToSource)
180 }
181 "both" | "bidi" | "bidirectional" | "duplex" | "all" => Ok(DirectionSpec::Both),
182 _ => Err(ParseDirectionError(s.to_string())),
183 }
184 }
185}
186
187#[derive(Debug, Clone, Default, Deserialize, Serialize)]
200pub struct PluginSpec {
201 #[serde(alias = "plugin", alias = "use")]
202 pub name: String,
203 #[serde(default)]
204 pub direction: DirectionSpec,
205 #[serde(default, rename = "as")]
209 pub alias: Option<String>,
210 #[serde(default)]
213 pub detach: Option<bool>,
214 #[serde(flatten, default)]
216 pub config: Map<String, Value>,
217}
218
219impl PluginSpec {
220 pub fn new(name: impl Into<String>, direction: DirectionSpec) -> Self {
221 Self {
222 name: name.into(),
223 direction,
224 alias: None,
225 detach: None,
226 config: Map::new(),
227 }
228 }
229
230 #[must_use]
231 pub fn named(mut self, alias: impl Into<String>) -> Self {
232 self.alias = Some(alias.into());
233 self
234 }
235
236 #[must_use]
237 pub fn detached(mut self, detach: bool) -> Self {
238 self.detach = Some(detach);
239 self
240 }
241
242 #[must_use]
243 pub fn with(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
244 self.config.insert(key.into(), value.into());
245 self
246 }
247
248 pub fn set(&mut self, key: impl Into<String>, value: impl Into<Value>) -> &mut Self {
249 self.config.insert(key.into(), value.into());
250 self
251 }
252}
253
254impl fmt::Display for PluginSpec {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 match &self.alias {
257 Some(alias) => write!(f, "{} ({}:{})", alias, self.name, self.direction),
258 None => write!(f, "{}:{}", self.name, self.direction),
259 }
260 }
261}