async_codegen/
lib.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#![allow(async_fn_in_trait)]
18
19//!
20//! A library for async code generation that imposes no ownership choices (can use borrowed or
21//! owned data) and is fully composable using generics and general-purpose structs.
22//!
23//! All syntax that can be generated is represented by a [`Writable`]. This is the core type of the
24//! library and it is quite simple. Any type that is supposed to be written to an output should
25//! implement this trait.
26//!
27//! Writable output is sent to an [`Output`]. Output consists of the direct I/O output as well as
28//! some context tied to it.
29//!
30
31use crate::context::{Context, ContextProvides, DynContext};
32
33/// Provides common and re-usable types, including types that are conceptual to this library and
34/// its type model.
35pub mod common;
36/// Using context for code generation requires importing this module
37pub mod context;
38pub(crate) mod cstyle_common;
39/// Java code syntax
40pub mod java;
41/// Rust code syntax
42pub mod rust;
43/// Utilities to help with testing and other purposes
44pub mod util;
45
46/// A type that can be written to an output stream.
47///
48/// This struct is typically implemented by generic structs whenever their fields are also
49/// `Writable`. In this way, writable types depend on each other and re-use common syntax.
50pub trait Writable<O: Output> {
51    /// Writes to the output. Returns the output's error upon failure.
52    ///
53    /// A writable can always be written multiple times. Because of this, the function takes a
54    /// borrowed reference.
55    async fn write_to(&self, output: &mut O) -> Result<(), O::Error>;
56}
57
58impl<'w, W, O> Writable<O> for &'w W
59where
60    W: Writable<O>,
61    O: Output,
62{
63    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
64        (**self).write_to(output).await
65    }
66}
67
68/// A sequence of writable types. Sequences are modeled in the library by this interface, so that
69/// different separators can implement [`SequenceAccept`].
70pub trait WritableSeq<O: Output> {
71    /// Writes each writable value in the sequence
72    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
73    where
74        S: SequenceAccept<O>;
75}
76
77impl<'w, W, O> WritableSeq<O> for &'w W
78where
79    O: Output,
80    W: WritableSeq<O>,
81{
82    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
83    where
84        S: SequenceAccept<O>,
85    {
86        (**self).for_each(sink).await
87    }
88}
89
90/// A collector for multiple writable values. This trait is the ingredient to [`WritableSeq`] that
91/// represents how the sequence is handled. For example, `accept` can be implemented by adding
92/// commas after each element but not the last, which can be obtained using `comma_separated`:
93/// ```
94/// # use async_codegen::common::SeparatedSeqAccept;
95/// # use async_codegen::{Output, WritableSeq};
96///
97/// async fn write_comma_separated<O, Seq>(output: &mut O, seq: Seq) -> Result<(), O::Error> where O: Output, Seq: WritableSeq<O> {
98///   let mut comma_sep = SeparatedSeqAccept::comma_separated(output);
99///   seq.for_each(&mut comma_sep).await?;
100///   Ok(())
101/// }
102/// ```
103pub trait SequenceAccept<O: Output> {
104    /// Writes a single writable type to this sink.
105    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
106    where
107        W: Writable<O>;
108}
109
110impl<'s, S, O> SequenceAccept<O> for &'s mut S
111where
112    O: Output,
113    S: SequenceAccept<O>,
114{
115    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
116    where
117        W: Writable<O>,
118    {
119        (**self).accept(writable).await
120    }
121}
122
123/// Code generation output. This is a high-level trait intended to represent wherever you're
124/// writing to, with associated context. It can be split into that context in order to separate the
125/// I/O stream itself.
126pub trait Output {
127    /// The I/O stream type
128    type Io<'o>: IoOutput<Error: Into<Self::Error>>
129    where
130        Self: 'o;
131    /// The context holder
132    type Ctx: Context;
133    /// The error type for write operations.
134    type Error;
135
136    /// Writes the given value to the output.
137    async fn write(&mut self, value: &str) -> Result<(), Self::Error>;
138
139    /// Splits into the context and the I/O stream, so that they can be used separately
140    fn split(&mut self) -> (Self::Io<'_>, &Self::Ctx);
141
142    /// Gets all the context associated with this output
143    fn context(&self) -> &Self::Ctx;
144
145    /// Gets a particular context value
146    fn get_ctx<T>(&self) -> &T
147    where
148        Self::Ctx: ContextProvides<T>,
149    {
150        self.context().provide()
151    }
152}
153
154impl<'o, O> Output for &'o mut O
155where
156    O: Output,
157{
158    type Io<'b>
159        = O::Io<'b>
160    where
161        Self: 'b;
162    type Ctx = O::Ctx;
163    type Error = O::Error;
164
165    async fn write(&mut self, value: &str) -> Result<(), Self::Error> {
166        (**self).write(value).await
167    }
168
169    fn split(&mut self) -> (Self::Io<'_>, &Self::Ctx) {
170        (**self).split()
171    }
172
173    fn context(&self) -> &Self::Ctx {
174        (**self).context()
175    }
176}
177
178/// An output that simply composes an owned I/O stream with a dynamic context
179pub struct SimpleOutput<I: IoOutput> {
180    pub io_output: I,
181    pub context: DynContext,
182}
183
184impl<I> Output for SimpleOutput<I>
185where
186    I: IoOutput,
187{
188    type Io<'b>
189        = &'b mut I
190    where
191        Self: 'b;
192    type Ctx = DynContext;
193    type Error = I::Error;
194
195    async fn write(&mut self, value: &str) -> Result<(), Self::Error> {
196        self.io_output.write(value).await
197    }
198
199    fn split(&mut self) -> (Self::Io<'_>, &Self::Ctx) {
200        (&mut self.io_output, &self.context)
201    }
202
203    fn context(&self) -> &Self::Ctx {
204        &self.context
205    }
206}
207
208/// The raw IO output.
209///
210/// This trait is not implemented by the library in a concrete way. Instead, depending on which
211/// async runtime you are using, you will have to implement it yourself.
212pub trait IoOutput {
213    /// The error type for write operations
214    type Error;
215    /// Writes a string to the output. Yields an error upon failure.
216    async fn write(&mut self, value: &str) -> Result<(), Self::Error>;
217}
218
219impl<'o, O> IoOutput for &'o mut O
220where
221    O: IoOutput,
222{
223    type Error = O::Error;
224
225    async fn write(&mut self, value: &str) -> Result<(), Self::Error> {
226        (**self).write(value).await
227    }
228}