1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use proclet::{op, pm1::StringLiteral, prelude::*, proclet, punctuated, Optional};
5
6#[proc_macro]
12pub fn str_block(input: TokenStream) -> TokenStream {
13 proclet(input, |input| {
14 let strings = punctuated(StringLiteral::parser(), Optional(op(","))).parse_all(input)?;
15
16 let str: String = strings.into_iter().map(|(s, _)| s.into_value()).collect();
18
19 let mut lines = str.lines();
20 let mut lines2 = lines.clone();
21 let Some(first) = lines.next() else {
22 return Ok(StringLiteral::new(String::new()));
24 };
25 let first = if first.trim().is_empty() {
27 let _ = lines2.next();
28 if let Some(second) = lines.next() {
29 second
30 } else {
31 return Ok(StringLiteral::new(String::new()));
33 }
34 } else {
35 first
36 };
37
38 let first_trimmed = first.trim_start();
39 let mut prefix = &first[..first.len() - first_trimmed.len()];
40 if !prefix.is_empty() {
41 for line in lines {
42 if !line.trim().is_empty() {
43 let ci = prefix
44 .chars()
45 .zip(line.chars())
46 .take_while(|(p, l)| p == l)
47 .fold(0, |ci, (p, _)| ci + p.len_utf8());
48 if ci < prefix.len() {
49 prefix = &prefix[..ci];
50 if prefix.is_empty() {
51 break;
52 }
53 }
54 }
55 }
56 }
57
58 let mut output = String::from(lines2.next().unwrap().strip_prefix(prefix).unwrap_or(""));
59 for line in lines2 {
60 output.push('\n');
61 output.push_str(line.strip_prefix(prefix).unwrap_or(""));
62 }
63 if str.ends_with('\n') {
64 output.push('\n');
65 }
66 Ok(StringLiteral::new(output))
67 })
68}