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}