async-codegen 0.12.1

Minimalist async-IO code generation framework.
Documentation
/*
 * Copyright © 2025 Anand Beh
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#![allow(async_fn_in_trait)]

//!
//! A library for async code generation that imposes no ownership choices (can use borrowed or
//! owned data) and is fully composable using generics and general-purpose structs.
//!
//! All syntax that can be generated is represented by a [`Writable`]. This is the core type of the
//! library and it is quite simple. Any type that is supposed to be written to an output should
//! implement this trait.
//!
//! Writable output is sent to an [`Output`]. Output consists of the direct I/O output as well as
//! some context tied to it.
//!

use crate::context::{Context, ContextProvides, DynContext};

/// Provides common and re-usable types, including types that are conceptual to this library and
/// its type model.
pub mod common;
/// Using context for code generation requires importing this module
pub mod context;
pub(crate) mod cstyle_common;
/// Java code syntax
pub mod java;
/// Rust code syntax
pub mod rust;
/// Utilities to help with testing and other purposes
pub mod util;

/// A type that can be written to an output stream.
///
/// This struct is typically implemented by generic structs whenever their fields are also
/// `Writable`. In this way, writable types depend on each other and re-use common syntax.
pub trait Writable<O: Output> {
    /// Writes to the output. Returns the output's error upon failure.
    ///
    /// A writable can always be written multiple times. Because of this, the function takes a
    /// borrowed reference.
    async fn write_to(&self, output: &mut O) -> Result<(), O::Error>;
}

impl<'w, W, O> Writable<O> for &'w W
where
    W: Writable<O>,
    O: Output,
{
    async fn write_to(&self, output: &mut O) -> Result<(), O::Error> {
        (**self).write_to(output).await
    }
}

/// A sequence of writable types. Sequences are modeled in the library by this interface, so that
/// different separators can implement [`SequenceAccept`].
pub trait WritableSeq<O: Output> {
    /// Writes each writable value in the sequence
    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
    where
        S: SequenceAccept<O>;
}

impl<'w, W, O> WritableSeq<O> for &'w W
where
    O: Output,
    W: WritableSeq<O>,
{
    async fn for_each<S>(&self, sink: &mut S) -> Result<(), O::Error>
    where
        S: SequenceAccept<O>,
    {
        (**self).for_each(sink).await
    }
}

/// A collector for multiple writable values. This trait is the ingredient to [`WritableSeq`] that
/// represents how the sequence is handled. For example, `accept` can be implemented by adding
/// commas after each element but not the last, which can be obtained using `comma_separated`:
/// ```
/// # use async_codegen::common::SeparatedSeqAccept;
/// # use async_codegen::{Output, WritableSeq};
///
/// async fn write_comma_separated<O, Seq>(output: &mut O, seq: Seq) -> Result<(), O::Error> where O: Output, Seq: WritableSeq<O> {
///   let mut comma_sep = SeparatedSeqAccept::comma_separated(output);
///   seq.for_each(&mut comma_sep).await?;
///   Ok(())
/// }
/// ```
pub trait SequenceAccept<O: Output> {
    /// Writes a single writable type to this sink.
    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
    where
        W: Writable<O>;
}

impl<'s, S, O> SequenceAccept<O> for &'s mut S
where
    O: Output,
    S: SequenceAccept<O>,
{
    async fn accept<W>(&mut self, writable: &W) -> Result<(), O::Error>
    where
        W: Writable<O>,
    {
        (**self).accept(writable).await
    }
}

/// Code generation output. This is a high-level trait intended to represent wherever you're
/// writing to, with associated context. It can be split into that context in order to separate the
/// I/O stream itself.
pub trait Output {
    /// The I/O stream type
    type Io<'o>: IoOutput<Error: Into<Self::Error>>
    where
        Self: 'o;
    /// The context holder
    type Ctx: Context;
    /// The error type for write operations.
    type Error;

    /// Writes the given value to the output.
    async fn write(&mut self, value: &str) -> Result<(), Self::Error>;

    /// Splits into the context and the I/O stream, so that they can be used separately
    fn split(&mut self) -> (Self::Io<'_>, &Self::Ctx);

    /// Gets all the context associated with this output
    fn context(&self) -> &Self::Ctx;

    /// Gets a particular context value
    fn get_ctx<T>(&self) -> &T
    where
        Self::Ctx: ContextProvides<T>,
    {
        self.context().provide()
    }
}

impl<'o, O> Output for &'o mut O
where
    O: Output,
{
    type Io<'b>
        = O::Io<'b>
    where
        Self: 'b;
    type Ctx = O::Ctx;
    type Error = O::Error;

    async fn write(&mut self, value: &str) -> Result<(), Self::Error> {
        (**self).write(value).await
    }

    fn split(&mut self) -> (Self::Io<'_>, &Self::Ctx) {
        (**self).split()
    }

    fn context(&self) -> &Self::Ctx {
        (**self).context()
    }
}

/// An output that simply composes an owned I/O stream with a dynamic context
pub struct SimpleOutput<I: IoOutput> {
    pub io_output: I,
    pub context: DynContext,
}

impl<I> Output for SimpleOutput<I>
where
    I: IoOutput,
{
    type Io<'b>
        = &'b mut I
    where
        Self: 'b;
    type Ctx = DynContext;
    type Error = I::Error;

    async fn write(&mut self, value: &str) -> Result<(), Self::Error> {
        self.io_output.write(value).await
    }

    fn split(&mut self) -> (Self::Io<'_>, &Self::Ctx) {
        (&mut self.io_output, &self.context)
    }

    fn context(&self) -> &Self::Ctx {
        &self.context
    }
}

/// The raw IO output.
///
/// This trait is not implemented by the library in a concrete way. Instead, depending on which
/// async runtime you are using, you will have to implement it yourself.
pub trait IoOutput {
    /// The error type for write operations
    type Error;
    /// Writes a string to the output. Yields an error upon failure.
    async fn write(&mut self, value: &str) -> Result<(), Self::Error>;
}

impl<'o, O> IoOutput for &'o mut O
where
    O: IoOutput,
{
    type Error = O::Error;

    async fn write(&mut self, value: &str) -> Result<(), Self::Error> {
        (**self).write(value).await
    }
}