Skip to main content

ploidy_core/codegen/
mod.rs

1//! Code generation output and file writing.
2//!
3//! This module defines the [`Code`] trait, which represents a
4//! single generated output file with a relative path and a
5//! content string.
6//!
7//! [`IntoCode`] converts codegen types into [`Code`]. Any type
8//! that implements [`Code`] automatically implements
9//! [`IntoCode`], so codegen types can implement either trait.
10//!
11//! [`write_to_disk`] takes an output directory and any [`IntoCode`]
12//! value, creates intermediate directories as needed, and writes the file.
13//!
14//! # Feature-gated blanket implementations
15//!
16//! - **`proc-macro2`**: `(T, TokenStream)` where `T: AsRef<str>`
17//!   formats the token stream with [prettyplease] and writes it
18//!   to the path given by `T`.
19//!
20//! [prettyplease]: https://docs.rs/prettyplease/latest/prettyplease/
21
22use std::path::Path;
23
24use miette::{Context, IntoDiagnostic};
25
26pub mod unique;
27
28pub use unique::{UniqueNames, WordSegments};
29
30pub fn write_to_disk(output: &Path, code: impl IntoCode) -> miette::Result<()> {
31    let code = code.into_code();
32    let path = output.join(code.path());
33    let string = code.into_string()?;
34    if let Some(parent) = path.parent() {
35        std::fs::create_dir_all(parent)
36            .into_diagnostic()
37            .with_context(|| format!("Failed to create directory `{}`", parent.display()))?;
38    }
39    std::fs::write(&path, string)
40        .into_diagnostic()
41        .with_context(|| format!("Failed to write `{}`", path.display()))?;
42    Ok(())
43}
44
45pub trait Code {
46    fn path(&self) -> &str;
47    fn into_string(self) -> miette::Result<String>;
48}
49
50#[cfg(feature = "proc-macro2")]
51impl<T: AsRef<str>> Code for (T, proc_macro2::TokenStream) {
52    fn path(&self) -> &str {
53        self.0.as_ref()
54    }
55
56    fn into_string(self) -> miette::Result<String> {
57        use quote::ToTokens;
58        let file = syn::parse2(self.1.into_token_stream()).into_diagnostic();
59        match file {
60            Ok(file) => Ok(prettyplease::unparse(&file)),
61            Err(err) => Err(err.context(format!("Failed to format `{}`", self.0.as_ref()))),
62        }
63    }
64}
65
66pub trait IntoCode {
67    type Code: Code;
68
69    fn into_code(self) -> Self::Code;
70}
71
72impl<T: Code> IntoCode for T {
73    type Code = T;
74
75    fn into_code(self) -> Self::Code {
76        self
77    }
78}