async_codegen/
common.rs

1/*
2 * Copyright © 2025 Anand Beh
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//!
18//!
19//! Provides common, re-usable types that work for any code generation purposes
20//!
21//! Some of the types in this module are conceptual and are related to the library itself, like
22//! [NoOp] or [CombinedBody]. Other types are syntax based, but their syntax is so general that
23//! they are useful for code generation in different languages.
24//!
25
26use super::{Output, SequenceAccept, Writable, WritableSeq};
27
28/// Empty writable
29pub struct NoOp;
30
31impl<O> Writable<O> for NoOp
32where
33    O: Output,
34{
35    async fn write_to(&self, _: &mut O) -> Result<(), O::Error> {
36        Ok(())
37    }
38}
39
40/// Empty writable sequence
41pub struct NoOpSeq;
42
43impl<O> WritableSeq<O> for NoOpSeq
44where
45    O: Output,
46{
47    async fn for_each<S: SequenceAccept<O>>(&self, _: &mut S) -> Result<(), O::Error> {
48        Ok(())
49    }
50}
51
52/// A combined writable that simply writes its tuple values out
53pub struct Combined<W1, W2>(pub W1, pub W2);
54
55impl<O, W1, W2> Writable<O> for Combined<W1, W2>
56where
57    O: Output,
58    W1: Writable<O>,
59    W2: Writable<O>,
60{
61    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
62        self.0.write_to(output).await?;
63        self.1.write_to(output).await
64    }
65}
66
67/// A combined writable sequence that consists of two other sequences
68pub struct CombinedSeq<S1, S2>(pub S1, pub S2);
69
70impl<O, S1, S2> WritableSeq<O> for CombinedSeq<S1, S2>
71where
72    O: Output,
73    S1: WritableSeq<O>,
74    S2: WritableSeq<O>,
75{
76    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
77    where
78        S: SequenceAccept<O>,
79    {
80        self.0.for_each(sink).await?;
81        self.1.for_each(sink).await
82    }
83}
84
85/// A singular converts a [`Writable`] into a single-element [`WritableSeq`]
86/// Its implementation just writes the tuple value.
87pub struct SingularSeq<W>(pub W);
88
89impl<O, W> WritableSeq<O> for SingularSeq<W>
90where
91    O: Output,
92    W: Writable<O>,
93{
94    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
95    where
96        S: SequenceAccept<O>,
97    {
98        sink.accept(&self.0).await
99    }
100}
101
102/// A struct that repeats a value multiple times in order to turn it into a [`WritableSeq`].
103/// This struct is indeed a generalization of [`SingularSeq`] (it is equivalent when `count = 1`).
104pub struct RepeatSeq<W> {
105    /// The number of repetitions
106    pub count: usize,
107    /// The element to repeat. Must be writable.
108    pub writable: W,
109}
110
111impl<O, W> WritableSeq<O> for RepeatSeq<W>
112where
113    O: Output,
114    W: Writable<O>,
115{
116    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
117    where
118        S: SequenceAccept<O>,
119    {
120        for _ in 0..self.count {
121            sink.accept(&self.writable).await?;
122        }
123        Ok(())
124    }
125}
126
127/// A sequence based on an array of writable elements
128pub struct ArrSeq<'a, W>(pub &'a [W]);
129
130impl<O, W> WritableSeq<O> for ArrSeq<'_, W>
131where
132    O: Output,
133    W: Writable<O>,
134{
135    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
136    where
137        S: SequenceAccept<O>,
138    {
139        for elem in self.0 {
140            sink.accept(elem).await?;
141        }
142        Ok(())
143    }
144}
145
146/// Turns a string or a string reference into a writable value.
147/// Can be used with [str], [String], or any type that implements `AsRef<str>`
148pub struct Str<STR>(pub STR);
149
150impl<O, STR> Writable<O> for Str<STR>
151where
152    O: Output,
153    STR: AsRef<str>,
154{
155    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
156        output.write(self.0.as_ref()).await
157    }
158}
159
160/// Like [ArrSeq], but specialized for string so that you don't need to wrap each element in [Str]
161pub struct StrArrSeq<'a>(pub &'a [&'a str]);
162
163impl<O> WritableSeq<O> for StrArrSeq<'_>
164where
165    O: Output,
166{
167    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
168    where
169        S: SequenceAccept<O>,
170    {
171        for elem in self.0 {
172            sink.accept(&Str(*elem)).await?;
173        }
174        Ok(())
175    }
176}
177
178/// A general purpose sequence acceptor that tracks whether any element is written and can
179/// write a separator between each element, in addition to an optional intro at the beginning.
180pub struct SeparatedSeqAccept<'o, O, Intro, Sep> {
181    /// The output.
182    pub output: &'o mut O,
183    /// The intro. Must be writable
184    pub intro: Intro,
185    /// The separator. Must be writable
186    pub separator: Sep,
187    /// Whether this instance wrote anything. Should be initially set to false.
188    pub wrote_any: bool,
189}
190
191impl<'o, O, Intro, Sep> SeparatedSeqAccept<'o, O, Intro, Sep> {
192    pub fn new(output: &'o mut O, intro: Intro, separator: Sep) -> Self {
193        Self {
194            output,
195            intro,
196            separator,
197            wrote_any: false,
198        }
199    }
200}
201
202impl<'o, O, Sep> SeparatedSeqAccept<'o, O, NoOp, Sep> {
203    pub fn no_intro(output: &'o mut O, separator: Sep) -> Self {
204        Self::new(output, NoOp, separator)
205    }
206}
207
208impl<'o, O> SeparatedSeqAccept<'o, O, NoOp, Str<&'static str>> {
209    /// Makes a sink for sequences that separates by `, `.
210    pub fn comma_separated(output: &'o mut O) -> Self {
211        Self::new(output, NoOp, Str(", "))
212    }
213}
214
215impl<'o, O, Intro, Sep> SequenceAccept<O> for SeparatedSeqAccept<'o, O, Intro, Sep>
216where
217    O: Output,
218    Intro: Writable<O>,
219    Sep: Writable<O>,
220{
221    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
222    where
223        W: Writable<O>,
224    {
225        if self.wrote_any {
226            self.separator.write_to(self.output).await?;
227        } else {
228            self.intro.write_to(self.output).await?;
229            self.wrote_any = true;
230        }
231        writable.write_to(self.output).await
232    }
233}
234
235/// A sequence implementation that takes elements in an array, and writes each element using
236/// the config. The config should implement [WritableDatumConfig].
237pub struct SeqArrViaConfig<'t, T, Config> {
238    pub data: &'t [T],
239    pub config: Config,
240}
241
242/// A helper object that writes other data, and not the object itself, on demand
243pub trait WritableDatumConfig<T, O>
244where
245    O: Output,
246{
247    /// Writes an individual data point.
248    async fn write_datum(&self, datum: &T, output: &mut O) -> Result<(), O::Error>;
249}
250
251impl<'t, T, Config, O> WritableSeq<O> for SeqArrViaConfig<'t, T, Config>
252where
253    O: Output,
254    Config: WritableDatumConfig<T, O>,
255{
256    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
257    where
258        S: SequenceAccept<O>,
259    {
260        struct WriteSingle<'t, T, Config> {
261            datum: &'t T,
262            config: &'t Config,
263        }
264        impl<'t, T, Config, O> Writable<O> for WriteSingle<'t, T, Config>
265        where
266            O: Output,
267            Config: WritableDatumConfig<T, O>,
268        {
269            async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
270                self.config.write_datum(self.datum, output).await
271            }
272        }
273        for datum in self.data {
274            sink.accept(&WriteSingle {
275                datum,
276                config: &self.config,
277            })
278            .await?;
279        }
280        Ok(())
281    }
282}
283
284/// A wrapper intended to be used as a [WritableDatumConfig].
285/// The tuple value should be a function that returns a writable for the data type.
286pub struct ExtractWritable<F>(pub F);
287
288impl<F, T, W, O> WritableDatumConfig<T, O> for ExtractWritable<F>
289where
290    O: Output,
291    for<'t> F: Fn(&'t T) -> W,
292    W: Writable<O>,
293{
294    async fn write_datum(&self, datum: &T, output: &mut O) -> Result<(), O::Error> {
295        let writable = (&self.0)(datum);
296        writable.write_to(output).await
297    }
298}