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>`
148#[derive(Copy, Clone, Debug)]
149pub struct Str<STR>(pub STR);
150
151impl<O, STR> Writable<O> for Str<STR>
152where
153    O: Output,
154    STR: AsRef<str>,
155{
156    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
157        output.write(self.0.as_ref()).await
158    }
159}
160
161/// Like [ArrSeq], but specialized for string so that you don't need to wrap each element in [Str]
162pub struct StrArrSeq<'a>(pub &'a [&'a str]);
163
164impl<O> WritableSeq<O> for StrArrSeq<'_>
165where
166    O: Output,
167{
168    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
169    where
170        S: SequenceAccept<O>,
171    {
172        for elem in self.0 {
173            sink.accept(&Str(*elem)).await?;
174        }
175        Ok(())
176    }
177}
178
179/// A general purpose sequence acceptor that tracks whether any element is written and can
180/// write a separator between each element, in addition to an optional intro at the beginning.
181pub struct SeparatedSeqAccept<'o, O, Intro, Sep> {
182    /// The output.
183    pub output: &'o mut O,
184    /// The intro. Must be writable
185    pub intro: Intro,
186    /// The separator. Must be writable
187    pub separator: Sep,
188    /// Whether this instance wrote anything. Should be initially set to false.
189    pub wrote_any: bool,
190}
191
192impl<'o, O, Intro, Sep> SeparatedSeqAccept<'o, O, Intro, Sep> {
193    pub fn new(output: &'o mut O, intro: Intro, separator: Sep) -> Self {
194        Self {
195            output,
196            intro,
197            separator,
198            wrote_any: false,
199        }
200    }
201}
202
203impl<'o, O, Sep> SeparatedSeqAccept<'o, O, NoOp, Sep> {
204    pub fn no_intro(output: &'o mut O, separator: Sep) -> Self {
205        Self::new(output, NoOp, separator)
206    }
207}
208
209impl<'o, O> SeparatedSeqAccept<'o, O, NoOp, Str<&'static str>> {
210    /// Makes a sink for sequences that separates by `, `.
211    pub fn comma_separated(output: &'o mut O) -> Self {
212        Self::new(output, NoOp, Str(", "))
213    }
214}
215
216impl<'o, O, Intro, Sep> SequenceAccept<O> for SeparatedSeqAccept<'o, O, Intro, Sep>
217where
218    O: Output,
219    Intro: Writable<O>,
220    Sep: Writable<O>,
221{
222    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
223    where
224        W: Writable<O>,
225    {
226        if self.wrote_any {
227            self.separator.write_to(self.output).await?;
228        } else {
229            self.intro.write_to(self.output).await?;
230            self.wrote_any = true;
231        }
232        writable.write_to(self.output).await
233    }
234}
235
236/// A general purpose sequence acceptor that surrounds every element written with the "before"
237/// and "after" writable elements.
238pub struct SurroundingSeqAccept<'o, O, Before, After> {
239    pub output: &'o mut O,
240    pub before: Before,
241    pub after: After,
242}
243
244impl<'o, O, Before, After> SurroundingSeqAccept<'o, O, Before, After> {
245    pub fn new(output: &'o mut O, before: Before, after: After) -> Self {
246        Self {
247            output,
248            before,
249            after,
250        }
251    }
252}
253
254impl<'o, O, Before, After> SequenceAccept<O> for SurroundingSeqAccept<'o, O, Before, After>
255where
256    O: Output,
257    Before: Writable<O>,
258    After: Writable<O>,
259{
260    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
261    where
262        W: Writable<O>,
263    {
264        self.before.write_to(self.output).await?;
265        writable.write_to(self.output).await?;
266        self.after.write_to(self.output).await
267    }
268}
269
270/// A sequence implementation that takes elements in an array, and writes each element using
271/// the config. The config should implement [SequenceConfig].
272pub struct SequenceViaConfig<'t, T, Config> {
273    pub data: &'t [T],
274    pub config: Config,
275}
276
277/// A helper object that writes other data, and not the object itself, on demand
278pub trait SequenceConfig<T, O>
279where
280    O: Output,
281{
282    /// Writes an individual data point.
283    async fn write_datum(&self, datum: &T, output: &mut O) -> Result<(), O::Error>;
284}
285
286impl<'t, T, Config, O> WritableSeq<O> for SequenceViaConfig<'t, T, Config>
287where
288    O: Output,
289    Config: SequenceConfig<T, O>,
290{
291    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
292    where
293        S: SequenceAccept<O>,
294    {
295        struct WriteSingle<'t, T, Config> {
296            datum: &'t T,
297            config: &'t Config,
298        }
299        impl<'t, T, Config, O> Writable<O> for WriteSingle<'t, T, Config>
300        where
301            O: Output,
302            Config: SequenceConfig<T, O>,
303        {
304            async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
305                self.config.write_datum(self.datum, output).await
306            }
307        }
308        for datum in self.data {
309            sink.accept(&WriteSingle {
310                datum,
311                config: &self.config,
312            })
313            .await?;
314        }
315        Ok(())
316    }
317}