Skip to main content

sel/
app.rs

1//! Typed builder for a ready-to-run pipeline.
2//!
3//! Encodes the invariant that positional selectors cannot be paired with stdin.
4
5use crate::context::Expander;
6use crate::format::Formatter;
7use crate::matcher::Matcher;
8use crate::sink::Sink;
9use crate::source::Source;
10
11/// Type-level marker: any source.
12pub trait SourceKind {}
13/// Type-level marker: sources that allow positional selectors.
14pub trait Seekable: SourceKind {}
15
16pub struct Seek;
17pub struct NonSeek;
18
19impl SourceKind for Seek {}
20impl SourceKind for NonSeek {}
21impl Seekable for Seek {}
22
23pub struct App<K: SourceKind> {
24    pub source: Box<dyn Source>,
25    pub matcher: Box<dyn Matcher>,
26    pub expander: Box<dyn Expander>,
27    pub formatter: Box<dyn Formatter>,
28    pub sink: Box<dyn Sink>,
29    _k: std::marker::PhantomData<K>,
30}
31
32/// Stage 1: pick a source.
33pub struct Stage1;
34
35impl Stage1 {
36    pub fn with_seekable_source(source: Box<dyn Source>) -> Stage2<Seek> {
37        Stage2 {
38            source,
39            _k: std::marker::PhantomData,
40        }
41    }
42    pub fn with_nonseekable_source(source: Box<dyn Source>) -> Stage2<NonSeek> {
43        Stage2 {
44            source,
45            _k: std::marker::PhantomData,
46        }
47    }
48}
49
50/// Stage 2: pick a matcher. Positional only allowed on `Seek`.
51pub struct Stage2<K: SourceKind> {
52    source: Box<dyn Source>,
53    _k: std::marker::PhantomData<K>,
54}
55
56impl<K: SourceKind> Stage2<K> {
57    pub fn with_matcher(self, matcher: Box<dyn Matcher>) -> Stage3<K> {
58        Stage3 {
59            source: self.source,
60            matcher,
61            _k: std::marker::PhantomData,
62        }
63    }
64}
65
66impl Stage2<Seek> {
67    /// Positional matcher — only available on seekable sources.
68    pub fn with_position_matcher(self, matcher: crate::matcher::PositionMatcher) -> Stage3<Seek> {
69        Stage3 {
70            source: self.source,
71            matcher: Box::new(matcher),
72            _k: std::marker::PhantomData,
73        }
74    }
75}
76
77pub struct Stage3<K: SourceKind> {
78    source: Box<dyn Source>,
79    matcher: Box<dyn Matcher>,
80    _k: std::marker::PhantomData<K>,
81}
82
83impl<K: SourceKind> Stage3<K> {
84    pub fn with_expander(self, expander: Box<dyn Expander>) -> Stage4<K> {
85        Stage4 {
86            source: self.source,
87            matcher: self.matcher,
88            expander,
89            _k: std::marker::PhantomData,
90        }
91    }
92}
93
94pub struct Stage4<K: SourceKind> {
95    source: Box<dyn Source>,
96    matcher: Box<dyn Matcher>,
97    expander: Box<dyn Expander>,
98    _k: std::marker::PhantomData<K>,
99}
100
101impl<K: SourceKind> Stage4<K> {
102    pub fn with_formatter(self, formatter: Box<dyn Formatter>) -> Stage5<K> {
103        Stage5 {
104            source: self.source,
105            matcher: self.matcher,
106            expander: self.expander,
107            formatter,
108            _k: std::marker::PhantomData,
109        }
110    }
111}
112
113pub struct Stage5<K: SourceKind> {
114    source: Box<dyn Source>,
115    matcher: Box<dyn Matcher>,
116    expander: Box<dyn Expander>,
117    formatter: Box<dyn Formatter>,
118    _k: std::marker::PhantomData<K>,
119}
120
121impl<K: SourceKind> Stage5<K> {
122    pub fn with_sink(self, sink: Box<dyn Sink>) -> App<K> {
123        App {
124            source: self.source,
125            matcher: self.matcher,
126            expander: self.expander,
127            formatter: self.formatter,
128            sink,
129            _k: std::marker::PhantomData,
130        }
131    }
132}