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 [Combined]. 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
29#[derive(Copy, Clone, Debug)]
30pub struct NoOp;
31
32impl<O> Writable<O> for NoOp
33where
34    O: Output,
35{
36    async fn write_to(&self, _: &mut O) -> Result<(), O::Error> {
37        Ok(())
38    }
39}
40
41/// Empty writable sequence
42#[derive(Copy, Clone, Debug)]
43pub struct NoOpSeq;
44
45impl<O> WritableSeq<O> for NoOpSeq
46where
47    O: Output,
48{
49    async fn for_each<S: SequenceAccept<O>>(&self, _: &mut S) -> Result<(), O::Error> {
50        Ok(())
51    }
52}
53
54/// A combined writable that simply writes its tuple values out
55#[derive(Clone, Debug)]
56pub struct Combined<W1, W2>(pub W1, pub W2);
57
58impl<O, W1, W2> Writable<O> for Combined<W1, W2>
59where
60    O: Output,
61    W1: Writable<O>,
62    W2: Writable<O>,
63{
64    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
65        self.0.write_to(output).await?;
66        self.1.write_to(output).await
67    }
68}
69
70/// A combined writable sequence that consists of two other sequences
71#[derive(Clone, Debug)]
72pub struct CombinedSeq<S1, S2>(pub S1, pub S2);
73
74impl<O, S1, S2> WritableSeq<O> for CombinedSeq<S1, S2>
75where
76    O: Output,
77    S1: WritableSeq<O>,
78    S2: WritableSeq<O>,
79{
80    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
81    where
82        S: SequenceAccept<O>,
83    {
84        self.0.for_each(sink).await?;
85        self.1.for_each(sink).await
86    }
87}
88
89/// A singular converts a [`Writable`] into a single-element [`WritableSeq`]
90/// Its implementation just writes the tuple value.
91#[derive(Clone, Debug)]
92pub struct SingularSeq<W>(pub W);
93
94impl<O, W> WritableSeq<O> for SingularSeq<W>
95where
96    O: Output,
97    W: Writable<O>,
98{
99    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
100    where
101        S: SequenceAccept<O>,
102    {
103        sink.accept(&self.0).await
104    }
105}
106
107/// A struct that repeats a value multiple times in order to turn it into a [`WritableSeq`].
108/// This struct is indeed a generalization of [`SingularSeq`] (it's equivalent when the count is 1).
109#[derive(Clone, Debug)]
110pub struct RepeatSeq<W>(pub usize, pub W);
111
112impl<O, W> WritableSeq<O> for RepeatSeq<W>
113where
114    O: Output,
115    W: Writable<O>,
116{
117    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
118    where
119        S: SequenceAccept<O>,
120    {
121        for _ in 0..self.0 {
122            sink.accept(&self.1).await?;
123        }
124        Ok(())
125    }
126}
127
128/// A sequence based on an array of writable elements
129#[derive(Debug)]
130pub struct ArrSeq<'a, W>(pub &'a [W]);
131
132impl<'a, W> Clone for ArrSeq<'a, W> {
133    fn clone(&self) -> Self {
134        Self(self.0)
135    }
136}
137
138impl<O, W> WritableSeq<O> for ArrSeq<'_, W>
139where
140    O: Output,
141    W: Writable<O>,
142{
143    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
144    where
145        S: SequenceAccept<O>,
146    {
147        for elem in self.0 {
148            sink.accept(elem).await?;
149        }
150        Ok(())
151    }
152}
153
154/// Turns a string or a string reference into a writable value.
155/// Can be used with [str], [String], or any type that implements `AsRef<str>`
156#[derive(Copy, Clone, Debug)]
157pub struct Str<STR>(pub STR);
158
159impl<O, STR> Writable<O> for Str<STR>
160where
161    O: Output,
162    STR: AsRef<str>,
163{
164    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
165        output.write(self.0.as_ref()).await
166    }
167}
168
169/// Like [ArrSeq], but specialized for string so that you don't need to wrap each element in [Str]
170#[derive(Clone, Debug)]
171pub struct StrArrSeq<'a>(pub &'a [&'a str]);
172
173impl<O> WritableSeq<O> for StrArrSeq<'_>
174where
175    O: Output,
176{
177    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
178    where
179        S: SequenceAccept<O>,
180    {
181        for elem in self.0 {
182            sink.accept(&Str(*elem)).await?;
183        }
184        Ok(())
185    }
186}
187
188/// A general purpose sequence acceptor that tracks whether any element is written and can
189/// write a separator between each element, in addition to an optional intro at the beginning.
190#[derive(Debug)]
191pub struct SeparatedSeqAccept<'o, O, Intro, Sep> {
192    /// The output.
193    pub output: &'o mut O,
194    /// The intro. Must be writable
195    pub intro: Intro,
196    /// The separator. Must be writable
197    pub separator: Sep,
198    /// Whether this instance wrote anything. Should be initially set to false.
199    pub wrote_any: bool,
200}
201
202impl<'o, O, Intro, Sep> SeparatedSeqAccept<'o, O, Intro, Sep> {
203    pub fn new(output: &'o mut O, intro: Intro, separator: Sep) -> Self {
204        Self {
205            output,
206            intro,
207            separator,
208            wrote_any: false,
209        }
210    }
211}
212
213impl<'o, O, Sep> SeparatedSeqAccept<'o, O, NoOp, Sep> {
214    pub fn no_intro(output: &'o mut O, separator: Sep) -> Self {
215        Self::new(output, NoOp, separator)
216    }
217}
218
219impl<'o, O> SeparatedSeqAccept<'o, O, NoOp, Str<&'static str>> {
220    /// Makes a sink for sequences that separates by `, `.
221    pub fn comma_separated(output: &'o mut O) -> Self {
222        Self::new(output, NoOp, Str(", "))
223    }
224}
225
226impl<'o, O, Intro, Sep> SequenceAccept<O> for SeparatedSeqAccept<'o, O, Intro, Sep>
227where
228    O: Output,
229    Intro: Writable<O>,
230    Sep: Writable<O>,
231{
232    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
233    where
234        W: Writable<O>,
235    {
236        if self.wrote_any {
237            self.separator.write_to(self.output).await?;
238        } else {
239            self.intro.write_to(self.output).await?;
240            self.wrote_any = true;
241        }
242        writable.write_to(self.output).await
243    }
244}
245
246/// A general purpose sequence acceptor that surrounds every element written with the "before"
247/// and "after" writable elements.
248#[derive(Debug)]
249pub struct SurroundingSeqAccept<'o, O, Before, After> {
250    pub output: &'o mut O,
251    pub before: Before,
252    pub after: After,
253}
254
255impl<'o, O, Before, After> SurroundingSeqAccept<'o, O, Before, After> {
256    pub fn new(output: &'o mut O, before: Before, after: After) -> Self {
257        Self {
258            output,
259            before,
260            after,
261        }
262    }
263}
264
265impl<'o, O, Before, After> SequenceAccept<O> for SurroundingSeqAccept<'o, O, Before, After>
266where
267    O: Output,
268    Before: Writable<O>,
269    After: Writable<O>,
270{
271    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
272    where
273        W: Writable<O>,
274    {
275        self.before.write_to(self.output).await?;
276        writable.write_to(self.output).await?;
277        self.after.write_to(self.output).await
278    }
279}
280
281/// A sequence implementation that takes elements in an array, and writes each element using
282/// the config. The config should implement [SequenceConfig].
283#[derive(Debug)]
284pub struct SequenceViaConfig<'t, T, Config> {
285    pub data: &'t [T],
286    pub config: Config,
287}
288
289impl<'t, T, Config> Clone for SequenceViaConfig<'t, T, Config>
290where
291    Config: Clone,
292{
293    fn clone(&self) -> Self {
294        Self {
295            data: self.data,
296            config: self.config.clone(),
297        }
298    }
299}
300
301/// A helper object that writes other data, and not the object itself, on demand
302pub trait SequenceConfig<T, O>
303where
304    O: Output,
305{
306    /// Writes an individual data point.
307    async fn write_datum(&self, datum: &T, output: &mut O) -> Result<(), O::Error>;
308}
309
310impl<'t, T, Config, O> WritableSeq<O> for SequenceViaConfig<'t, T, Config>
311where
312    O: Output,
313    Config: SequenceConfig<T, O>,
314{
315    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
316    where
317        S: SequenceAccept<O>,
318    {
319        struct WriteSingle<'t, T, Config> {
320            datum: &'t T,
321            config: &'t Config,
322        }
323        impl<'t, T, Config, O> Writable<O> for WriteSingle<'t, T, Config>
324        where
325            O: Output,
326            Config: SequenceConfig<T, O>,
327        {
328            async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
329                self.config.write_datum(self.datum, output).await
330            }
331        }
332        for datum in self.data {
333            sink.accept(&WriteSingle {
334                datum,
335                config: &self.config,
336            })
337            .await?;
338        }
339        Ok(())
340    }
341}
342
343/// Runs the given writable or sequence, but places the future inside [Box::pin].
344/// This layer of indirection helps to break recursion issues in async code.
345///
346/// (This struct can be used as either a writable or a sequence, depending on its contents)
347pub struct BoxedIndirection<W>(pub W);
348
349impl<O, W> Writable<O> for BoxedIndirection<W>
350where
351    O: Output,
352    W: Writable<O>,
353{
354    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
355        Box::pin(self.0.write_to(output)).await
356    }
357}
358
359impl<O, W> WritableSeq<O> for BoxedIndirection<W>
360where
361    O: Output,
362    W: WritableSeq<O>,
363{
364    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
365    where
366        S: SequenceAccept<O>,
367    {
368        Box::pin(self.0.for_each(sink)).await
369    }
370}