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