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