async_codegen/rust/mod.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//! Rust syntax elements.
19//!
20//! Note that no checking exists to make sure the elements are used correctly, i.e. the correct
21//! combination of structs. Instead, the library user is expected to have basic knowledge of how
22//! Rust syntax is composed, and to combine the structs in this module likewise.
23//!
24//! Example:
25//!
26//! ```
27//! # use async_codegen::common::{CombinedSeq, NoOpSeq, SingularSeq, Str};
28//! # use async_codegen::{Output, Writable};
29//! # use async_codegen::rust::{CanHaveAttributes, CfgAttr, Deprecated, FunctionBodyImplement, FunctionDef, FunctionParam, ModPub, MustUse, NoMangle, Parameterized};
30//!
31//! async fn write_function<O>(output: &mut O) -> Result<(), O::Error> where O: Output {
32//! // For more advanced usage, you can replace Str("") by other Writable implementations
33//! let function_def = FunctionDef {
34//! mods: SingularSeq(ModPub),
35//! name: Str("my_func"),
36//! args: CombinedSeq(
37//! SingularSeq(FunctionParam(Str("var1"), Str("Type"))),
38//! SingularSeq(FunctionParam(Str("var2"), Parameterized::new(Str("Option"), SingularSeq(Str("bool")))))
39//! ),
40//! return_type: Parameterized::new(Str("Box"), SingularSeq(Str("str"))),
41//! where_conds: NoOpSeq,
42//! body: FunctionBodyImplement(Str("todo!()"))
43//! };
44//! function_def.write_to(output).await
45//! // Will render as:
46//! /*
47//! pub fn my_func(var1: Type, var2: Option<bool>) -> Box<str> {
48//! todo!()
49//! }
50//! */
51//! }
52//! ```
53//!
54
55use crate::common::{Combined, NoOp, NoOpSeq, Str, SurroundingSeqAccept};
56use crate::{Output, SequenceAccept, Writable};
57use std::fmt::Debug;
58
59mod syntax;
60#[cfg(test)]
61mod tests;
62
63/// All possible Rust editions.
64/// This is the only type in this module meant to be used as context, and not as a writable itself.
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
66#[non_exhaustive]
67pub enum Edition {
68 /// This Rust edition is declared for usability purposes. However, not all [crate::Writable]
69 /// implementations are guaranteed to work with it.
70 Rust2015,
71 Rust2018,
72 Rust2021,
73 Rust2024,
74}
75
76/// An attribute enabled conditionally, i.e. `#[cfg_attr(Cond, Attr)]`
77#[derive(Clone, Debug)]
78pub struct CfgAttr<Cond, Attr>(pub Cond, pub Attr);
79
80/// A cfg attribute. Renders as `cfg(Cond)`.
81#[derive(Clone, Debug)]
82pub struct Cfg<Cond>(pub Cond);
83
84/// A cfg condition for targeting an OS, OS family, or architecture. For example:
85/// ```
86/// # use async_codegen::common::{NoOpSeq, SingularSeq, Str};
87/// # use async_codegen::context::EmptyContext;
88/// # use async_codegen::rust::{FunctionBodyDeclare, Cfg, FunctionDef, Target, CanHaveAttributes};
89/// # use async_codegen::util::InMemoryOutput;
90/// let function = FunctionDef {
91/// mods: NoOpSeq,
92/// name: Str("conditional_func"),
93/// args: NoOpSeq,
94/// return_type: Str("()"),
95/// where_conds: NoOpSeq,
96/// body: FunctionBodyDeclare
97/// }.with_attributes(
98/// SingularSeq(Cfg(Target::Os(Str("linux"))))
99/// );
100/// let string = InMemoryOutput::print_output(EmptyContext, &function);
101/// assert_eq!("#[cfg(target_os = \"linux\")]\nfn conditional_func() -> ();\n", string);
102/// ```
103#[derive(Clone, Debug)]
104pub enum Target<Value> {
105 Os(Value),
106 Family(Value),
107 Arch(Value),
108}
109
110/// The no mangle attribute.
111///
112/// Requires that the context satisfies [ContextProvides] for [Edition], because in Rust 2024 and
113/// beyond, the no-mangle attribute is an unsafe attribute.
114#[derive(Clone, Debug)]
115pub struct NoMangle;
116
117/// The attribute content for `allow(...)`. The tuple value must be a sequence.
118#[derive(Clone, Debug)]
119pub struct AllowLints<Lints>(pub Lints);
120
121/// The deprecated attribute. The three variants of this enum correspond to the deprecated
122/// attribute's multiple ways of being specified. See:
123/// https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute
124#[derive(Clone, Debug)]
125pub enum Deprecated<Msg, Since = NoOp> {
126 Basic,
127 Message(Msg),
128 Full { since: Since, note: Msg },
129}
130
131impl Default for Deprecated<NoOp, NoOp> {
132 fn default() -> Self {
133 Self::Basic
134 }
135}
136
137impl Deprecated<NoOp, NoOp> {
138 pub fn basic() -> Self {
139 Self::Basic
140 }
141}
142
143impl<Msg> Deprecated<Msg> {
144 pub fn with_message(msg: Msg) -> Self {
145 Self::Message(msg)
146 }
147}
148
149/// The must_use attribute
150#[derive(Clone, Debug)]
151pub struct MustUse;
152
153/// The public modifier
154#[derive(Clone, Debug)]
155pub struct ModPub;
156
157/// The extern modifier, with the ABI selected as the tuple value.
158///
159/// This struct includes `unsafe`. Since Rust 2024, the unsafe keyword is required for extern
160/// functions, and before Rust 2024 it is optional. To make it easy to generate code targeting
161/// multiple editions, we unconditionally emit the "unsafe" keyword alongside "extern".
162#[derive(Clone, Debug)]
163pub struct ModUnsafeExtern<Abi>(pub Abi);
164
165/// A let statement. This statement includes the semicolon and a new line.
166#[derive(Clone, Debug)]
167pub struct LetStmt<Variable, Expr>(pub Variable, pub Expr);
168
169/// An item attached to an associated container, via "::".
170/// The output will look like `Cont::Item`.
171#[derive(Clone, Debug)]
172pub struct AssociatedItem<Cont, Item>(pub Cont, pub Item);
173
174/// A question mark following another expression.
175#[derive(Clone, Debug)]
176pub struct QuestionMarkAfter<Expr>(pub Expr);
177
178/// Wraps an expression in `Ok(EXPR)`.
179#[derive(Clone, Debug)]
180pub struct OkResultOf<Expr>(pub Expr);
181
182/// Uses the `as` expression to perform a qualified trait cast (ready for a method call).
183/// I.e., this will render as `<Type as Trait>`.
184#[derive(Clone, Debug)]
185pub struct TypeAsTrait<Type, Trait>(pub Type, pub Trait);
186
187/// Declaration of an extern block, i.e. for FFI.
188/// In Rust 2024 and later, the unsafe keyword must be added for extern blocks. Thus, this struct
189/// requires that the context satisfies [ContextProvides] for [Edition].
190#[derive(Clone, Debug)]
191pub struct ExternBlock<Abi, Body> {
192 /// The ABI chosen. Must be writable
193 pub abi: Abi,
194 /// The body of the extern block. Must be writable
195 pub body: Body,
196}
197
198impl<Abi, Body> CanHaveAttributes for ExternBlock<Abi, Body> {
199 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
200 WithAttributes {
201 attr,
202 separator: "\n",
203 value: self,
204 }
205 }
206}
207
208/// Places the expression inside an unsafe block.
209/// Adds new lines inside the brackets, wrapping the inner expression.
210#[derive(Clone, Debug)]
211pub struct UnsafeBlock<Expr>(pub Expr);
212
213/// Writes a closure.
214/// Adds new lines inside the brackets, wrapping the inner expression.
215#[derive(Clone, Debug)]
216pub struct Closure<InputVars, Expr> {
217 /// The input variables.
218 /// Should be a sequence. They will be comma separated and placed within the pipes.
219 /// To use no input variables, use [NoOpSeq].
220 pub input_vars: InputVars,
221 /// The expression inside the closure block.
222 pub inside_block: Expr,
223}
224
225/// Performs a call to a function inside code.
226#[derive(Clone, Debug)]
227pub struct FunctionCall<Recv, Name, Args> {
228 /// The function receiver
229 pub receiver: Recv,
230 /// Whether the function is associated, false if it's a method
231 pub is_assoc: bool,
232 /// The function name
233 pub name: Name,
234 /// The arguments. Must be a sequence
235 pub args: Args,
236}
237
238/// A function declaration
239#[derive(Clone, Debug)]
240pub struct FunctionDef<Mods, Name, Args, Return, Where, Body> {
241 /// The modifiers. Must be a sequence.
242 pub mods: Mods,
243 /// The function name. Type variables can be declared here via [Parameterized]
244 pub name: Name,
245 /// The arguments. Must be a sequence
246 pub args: Args,
247 /// The return type, i.e. after the `->` arrow
248 pub return_type: Return,
249 /// The "where" conditions. Must be a sequence. Set to [NoOp] to disable.
250 /// Will render as `where C1, C2, C3, ...` where CX is a value in the sequence.
251 pub where_conds: Where,
252 /// The function body.
253 /// To only declare the function, this must be `;` so use [FunctionBodyDeclare]
254 /// To implement the function, use [FunctionBodyImplement]
255 pub body: Body,
256}
257
258impl<Mods, Name, Args, Return, Where, Body> CanHaveAttributes
259 for FunctionDef<Mods, Name, Args, Return, Where, Body>
260{
261 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
262 WithAttributes {
263 attr,
264 separator: "\n",
265 value: self,
266 }
267 }
268}
269
270/// Declares a function body. This is equivalent to just a semicolon.
271#[derive(Clone, Debug)]
272pub struct FunctionBodyDeclare;
273
274/// Implements a function body. Places the contents inside brackets
275#[derive(Clone, Debug)]
276pub struct FunctionBodyImplement<Inner>(pub Inner);
277
278/// A function pointer. Can be used for `fn`, `Fn`, `FnMut`, and `FnOnce`.
279///
280/// Example:
281/// ```
282/// # use async_codegen::common::{SingularSeq, Str};
283/// # use async_codegen::context::EmptyContext;
284/// # use async_codegen::rust::{FunctionPtr, FunctionPtrKind};
285/// # use async_codegen::util::InMemoryOutput;
286/// let function_ptr = FunctionPtr {
287/// kind: FunctionPtrKind::FnMut,
288/// args: SingularSeq(Str("String")),
289/// return_type: Str("bool")
290/// };
291/// let string = InMemoryOutput::print_output(EmptyContext, &function_ptr);
292/// assert_eq!("FnMut(String) -> bool", string);
293/// ```
294#[derive(Clone, Debug)]
295pub struct FunctionPtr<Args, Return> {
296 /// The function pointer kind
297 pub kind: FunctionPtrKind,
298 /// The arguments. Must be a sequence
299 pub args: Args,
300 /// The return type, i.e. after the `->` arrow
301 pub return_type: Return,
302}
303
304/// The kind of function type
305#[derive(Clone, Debug)]
306pub enum FunctionPtrKind {
307 /// An `fn` pointer. E.g. `fn(String) -> bool`.
308 FnPtr,
309 /// Represents [Fn]
310 Fn,
311 /// Represents [FnMut]
312 FnMut,
313 /// Represents [FnOnce]
314 FnOnce,
315}
316
317/// Renders as `Type=Value`. Intended to be used as a type argument, to specify associated types.
318#[derive(Clone, Debug)]
319pub struct AssociatedTypeEquals<Type, Value>(pub Type, pub Value);
320
321/// Adds a "dyn " before a type expression.
322#[derive(Clone, Debug)]
323pub struct DynOf<Type>(pub Type);
324
325/// Adds a "&" before a type expression
326#[derive(Clone, Debug)]
327pub struct RefOf<Type>(pub Type);
328
329/// Adds an "impl " before a type expression
330pub struct ImplOf<Type>(pub Type);
331
332/// Adds a reference with a lifetime before a type expression, i.e. `&'<lifetime> <type>`
333#[derive(Clone, Debug)]
334pub struct LifetimedRefOf<'l, Type>(pub &'l str, pub Type);
335
336/// Declares an associated type, rendering as `type VarName = Value;`.
337/// Adds new lines before and after.
338#[derive(Clone, Debug)]
339pub struct AssociatedTypeDef<VarName, Value>(pub VarName, pub Value);
340
341/// The declaration of a trait
342#[derive(Clone, Debug)]
343pub struct TraitDef<Mods, Name, TypeVars, SuperTraits, Body> {
344 /// The trait modifiers, e.g. visibility. Must be a sequence.
345 pub mods: Mods,
346 /// The name of the trait
347 pub name: Name,
348 /// The type variables. Must be a sequence
349 pub type_variables: TypeVars,
350 /// The super traits. Must be a sequence
351 pub super_traits: SuperTraits,
352 /// The trait definition's body. Use [NoOp] if none exists.
353 pub body: Body,
354}
355
356impl<Mods, Name, TypeVars, SuperTraits, Body> CanHaveAttributes
357 for TraitDef<Mods, Name, TypeVars, SuperTraits, Body>
358{
359 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
360 WithAttributes {
361 attr,
362 separator: "\n",
363 value: self,
364 }
365 }
366}
367
368/// The implementation declaration for a trait, applying to a certain receiver.
369#[derive(Clone, Debug)]
370pub struct TraitImpl<TypeVars, Trait, Recv, Where, Body> {
371 /// The type variables to use for the impl block itself. All type variables that appear later
372 /// on the trait or the receiver must be declared here, per Rust language rules.
373 ///
374 /// This field must be a sequence.
375 pub type_variables: TypeVars,
376 /// The trait being implemented
377 pub the_trait: Trait,
378 /// The receiver for which it is implemented
379 pub receiver: Recv,
380 /// The "where" conditions. Must be a sequence. Set to [NoOpSeq] to disable.
381 /// Will render as `where C1, C2, C3, ...` where CX is a value in the sequence.
382 pub where_conds: Where,
383 /// The body. Use [NoOp] if none exists.
384 pub body: Body,
385}
386
387impl<TypeVars, Trait, Recv, Where, Body> CanHaveAttributes for TraitImpl<TypeVars, Trait, Recv, Where, Body> {
388 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
389 WithAttributes {
390 attr,
391 separator: "\n",
392 value: self,
393 }
394 }
395}
396
397/// The declaration of a struct.
398#[derive(Clone, Debug)]
399pub struct StructDef<Mods, Name, Elements> {
400 /// The struct modifiers. Must be a sequence.
401 pub mods: Mods,
402 /// The kind of the struct.
403 ///
404 /// It is suggested to use either a [NamedTuple] or [StructCall]. A semicolon will be
405 /// automatically added afterward, as is needed for tuple structs, and this semicolon will not
406 /// affect structs with named fields.
407 pub kind: StructKind<Name, Elements>,
408}
409
410impl<Mods, Name, Elements> CanHaveAttributes for StructDef<Mods, Name, Elements> {
411 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
412 WithAttributes {
413 attr,
414 separator: "\n",
415 value: self,
416 }
417 }
418}
419
420/// Completes the struct definition as either a named tuple or a struct with named fields.
421#[derive(Clone, Debug)]
422pub enum StructKind<Name, Elements> {
423 /// A named tuple. This will function similarly to [NamedTuple], except a semicolon will
424 /// be added afterward.
425 ///
426 /// `Name` must be writable, and `Elements` must be a writable sequence for the tuple arguments.
427 Tuple(Name, Elements),
428 /// A struct with named fields. This will function similarly to [StructCall].
429 ///
430 /// `Name` must be writable, and `Elements` must be writable sequence for the struct fields.
431 NamedFields(Name, Elements),
432}
433
434/// The construction or deconstruction of a struct.
435///
436/// When rendered, will use the format `Name { Body }`. Spaces will be added automatically.
437///
438/// This should **not** be used for tuple structs, for that see [NamedTuple].
439#[derive(Clone, Debug)]
440pub struct StructCall<Name, Body> {
441 /// The struct name. Must be writable.
442 ///
443 /// If you are declaring a struct for the first time, you can use [Parameterized] in order
444 /// to declare type variables.
445 pub name: Name,
446 /// The body. Must be writable.
447 ///
448 /// It is suggested to use [StructFields] for multiple fields, or [DeclareField] for just one.
449 pub body: Body,
450}
451
452/// Named struct fields. This will place every field on a new line with a comma afterward.
453/// It is recommended that the sequence should pass [DeclareField].
454///
455/// If you have a single field, you can skip using a sequence and just use [DeclareField] directly.
456pub struct StructFields<Fields>(pub Fields);
457
458/// Declares a single field within a struct. Renders as `Name: Value`.
459///
460/// Does not add attributes. If you want to use attributes for declaration purposes, you can use
461/// [CanHaveAttributes::with_attributes] on this field.
462pub struct DeclareField<Name, Value>(pub Name, pub Value);
463
464impl<Name, Value> CanHaveAttributes for DeclareField<Name, Value> {
465 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
466 WithAttributes {
467 attr,
468 separator: "\n",
469 value: self,
470 }
471 }
472}
473
474/// A named tuple type.
475///
476/// Renders as `Name(A1, A2, A3, ...)` where AX is part of the argument sequence.
477/// If no arguments exist, will render only as `Name` (i.e., a unit struct).
478pub struct NamedTuple<Name, Args> {
479 pub name: Name,
480 pub args: Args,
481}
482
483/// An anonymous tuple type. This struct's tuple value must be a sequence.
484///
485/// Renders as `(A1, A2, A3, ...)` where AX is part of the argument sequence.
486#[derive(Clone, Debug)]
487pub struct AnonTuple<Args>(pub Args);
488
489/// The unit type, i.e. `()`
490pub type UnitType = AnonTuple<NoOpSeq>;
491
492impl AnonTuple<NoOpSeq> {
493 /// Creates
494 pub fn unit() -> Self {
495 Self(NoOpSeq)
496 }
497}
498
499/// Adds attributes to ANY item.
500///
501/// The first tuple value must be a sequence. The second must be a writable value. This struct
502/// is typically constructed via [CanHaveAttributes::with_attributes].
503///
504/// Rust attributes can be put in many places, so this enables you to add attributes to any
505/// writable item. For example, adding attributes to function parameters can be done like so:
506///
507/// ```rust
508/// # use async_codegen::common::{SingularSeq, Str};
509/// # use async_codegen::context::EmptyContext;
510/// # use async_codegen::rust::{Cfg, FunctionParam, MustUse, Target, WithAttributes, CanHaveAttributes};
511/// # use async_codegen::util::InMemoryOutput;
512///
513/// let function_param = FunctionParam(Str("conditional_param"), Str("Fd")).with_attributes(
514/// SingularSeq(Cfg(Target::Os(Str("linux"))))
515/// );
516/// let string = InMemoryOutput::print_output(EmptyContext, &function_param);
517/// assert_eq!("#[cfg(target_os = \"linux\")] conditional_param: Fd", string);
518/// ```
519#[derive(Clone, Debug)]
520pub struct WithAttributes<Attr, Value> {
521 pub attr: Attr,
522 /// The separator. Usually a space or a new line, depending on what the target value is
523 pub separator: &'static str,
524 /// The value
525 pub value: Value,
526}
527
528/// A writable that can have attributes attached to it
529pub trait CanHaveAttributes: Sized {
530 /// Adds attributes to this writable
531 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self>;
532}
533
534/// Defines an enum.
535///
536/// In order to use or refer to an enum, you can use [AssociatedItem] together with [NamedTuple]
537/// or [StructCall].
538pub struct EnumDef<Mods, Name, Entries> {
539 /// The modifiers on the type. Must be a sequence.
540 pub mods: Mods,
541 /// The name of the enum
542 pub name: Name,
543 /// The enum entries. Must be a sequence, each entry will be written on a new line with a comma
544 ///
545 /// As for the entries themselves, it is suggested to use [NamedTuple] or [StructCall]
546 /// depending on which kind of enum entry you want to create.
547 pub entries: Entries,
548}
549
550impl<Mods, Name, Entries> CanHaveAttributes for EnumDef<Mods, Name, Entries> {
551 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
552 WithAttributes {
553 attr,
554 separator: "\n",
555 value: self,
556 }
557 }
558}
559
560/// A type argument-parameterized expression. Used in relation to parameterized names and their
561/// arguments. Examples: `function_name<args>`, `TypeName<'lifetime, args>`, `MyType<Assoc=Value>`.
562///
563/// If no type args exist, [NoOpSeq] should be used.
564#[derive(Clone, Debug)]
565pub struct Parameterized<Name, TypeArgs> {
566 name: Name,
567 type_args: TypeArgs,
568}
569
570impl<Name, TypeArgs> Parameterized<Name, TypeArgs> {
571 /// Initializes an instance
572 pub fn new(name: Name, type_args: TypeArgs) -> Self {
573 Self { name, type_args }
574 }
575}
576
577/// A type variable with a sequence of bounds.
578/// Will render as `TypeVar: B1 + B2 + ...`
579#[derive(Clone, Debug)]
580pub struct BoundedTypeVar<TypeVar, Bounds>(pub TypeVar, pub Bounds);
581
582/// A standalone lifetime, intended to be used as a type argument or variable
583#[derive(Clone, Debug)]
584pub struct Lifetime<'l>(pub &'l str);
585
586/// Renders an individual function parameter, `Name: Type`
587#[derive(Clone, Debug)]
588pub struct FunctionParam<Name, Type>(pub Name, pub Type);
589
590impl<Name, Type> CanHaveAttributes for FunctionParam<Name, Type> {
591 fn with_attributes<Attr>(self, attr: Attr) -> WithAttributes<Attr, Self> {
592 WithAttributes {
593 attr,
594 separator: " ",
595 value: self,
596 }
597 }
598}
599
600/// A sequence acceptor that writes attributes. Every attribute will be surrounded with "#[]"
601#[derive(Debug)]
602pub struct AttributesAccept<'o, O, Sep> {
603 inner: SurroundingSeqAccept<'o, O, Str<&'static str>, Combined<Str<&'static str>, Sep>>,
604}
605
606impl<'o, O> AttributesAccept<'o, O, Str<&'static str>> {
607 pub fn multiline(output: &'o mut O) -> Self {
608 Self::with_separator(output, "\n")
609 }
610}
611
612impl<'o, O> AttributesAccept<'o, O, Str<&'static str>> {
613 pub fn with_separator(output: &'o mut O, separator: &'static str) -> Self {
614 Self {
615 inner: SurroundingSeqAccept::new(output, Str("#["), Combined(Str("]"), Str(separator))),
616 }
617 }
618}
619
620impl<'o, O, Sep> SequenceAccept<O> for AttributesAccept<'o, O, Sep>
621where
622 O: Output,
623 Sep: Writable<O>,
624{
625 async fn accept<W: Writable<O>>(&mut self, writable: &W) -> Result<(), O::Error> {
626 self.inner.accept(writable).await
627 }
628}