Skip to main content

clifford_codegen/codegen/
format.rs

1//! Code formatting utilities.
2//!
3//! This module provides utilities for formatting generated Rust code
4//! using rustfmt.
5
6use proc_macro2::TokenStream;
7use std::io::Write;
8use std::process::{Command, Stdio};
9
10/// Formats a token stream using rustfmt.
11///
12/// Returns the formatted code as a string. If rustfmt is not available
13/// or fails, returns the unformatted token stream as a string.
14///
15/// # Example
16///
17/// ```
18/// use quote::quote;
19/// use clifford_codegen::codegen::format_tokens;
20///
21/// let tokens = quote! {
22///     pub struct Foo { x: i32 }
23/// };
24///
25/// let formatted = format_tokens(&tokens);
26/// // formatted will be properly indented and spaced
27/// ```
28pub fn format_tokens(tokens: &TokenStream) -> String {
29    let unformatted = tokens.to_string();
30    format_code(&unformatted).unwrap_or(unformatted)
31}
32
33/// Formats Rust code using rustfmt.
34///
35/// Returns `None` if rustfmt is not available or fails.
36fn format_code(code: &str) -> Option<String> {
37    let mut child = Command::new("rustfmt")
38        .arg("--edition=2024")
39        .stdin(Stdio::piped())
40        .stdout(Stdio::piped())
41        .stderr(Stdio::null())
42        .spawn()
43        .ok()?;
44
45    // Write code to stdin
46    if let Some(mut stdin) = child.stdin.take() {
47        stdin.write_all(code.as_bytes()).ok()?;
48    }
49
50    // Read formatted output
51    let output = child.wait_with_output().ok()?;
52
53    if output.status.success() {
54        String::from_utf8(output.stdout).ok()
55    } else {
56        None
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use quote::quote;
64
65    #[test]
66    fn formats_simple_struct() {
67        let tokens = quote! {
68            pub struct Foo{x:i32,y:i32}
69        };
70
71        let formatted = format_tokens(&tokens);
72
73        // Check that it compiles (basic structure is preserved)
74        assert!(formatted.contains("struct Foo"));
75        assert!(formatted.contains("x"));
76        assert!(formatted.contains("y"));
77    }
78
79    #[test]
80    fn handles_complex_code() {
81        let tokens = quote! {
82            impl<T: Float> Vector<T> {
83                pub fn new(x: T, y: T) -> Self {
84                    Self { x, y }
85                }
86
87                pub fn x(&self) -> T {
88                    self.x
89                }
90            }
91        };
92
93        let formatted = format_tokens(&tokens);
94        assert!(formatted.contains("impl<T: Float> Vector<T>"));
95        assert!(formatted.contains("pub fn new"));
96    }
97}