ifmt.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. use proc_macro2::{Span, TokenStream};
  2. use quote::{quote, ToTokens};
  3. use syn::{
  4. parse::{Parse, ParseStream},
  5. *,
  6. };
  7. pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
  8. // build format_literal
  9. let mut format_literal = String::new();
  10. let mut expr_counter = 0;
  11. for segment in input.segments.iter() {
  12. match segment {
  13. Segment::Literal(s) => format_literal += &s,
  14. Segment::Formatted {
  15. format_args,
  16. segment,
  17. } => {
  18. format_literal += "{";
  19. match segment {
  20. FormattedSegment::Expr(_) => {
  21. format_literal += &expr_counter.to_string();
  22. expr_counter += 1;
  23. }
  24. FormattedSegment::Ident(ident) => {
  25. format_literal += &ident.to_string();
  26. }
  27. }
  28. format_literal += ":";
  29. format_literal += format_args;
  30. format_literal += "}";
  31. }
  32. }
  33. }
  34. let positional_args = input.segments.iter().filter_map(|seg| {
  35. if let Segment::Formatted { segment, .. } = seg {
  36. if let FormattedSegment::Expr(expr) = segment {
  37. Some(expr)
  38. } else {
  39. None
  40. }
  41. } else {
  42. None
  43. }
  44. });
  45. let named_args = input.segments.iter().filter_map(|seg| {
  46. if let Segment::Formatted { segment, .. } = seg {
  47. if let FormattedSegment::Ident(ident) = segment {
  48. Some(quote! {#ident = #ident})
  49. } else {
  50. None
  51. }
  52. } else {
  53. None
  54. }
  55. });
  56. Ok(quote! {
  57. format_args!(
  58. #format_literal
  59. #(, #positional_args)*
  60. #(, #named_args)*
  61. )
  62. })
  63. }
  64. #[allow(dead_code)] // dumb compiler does not see the struct being used...
  65. #[derive(Debug)]
  66. pub struct IfmtInput {
  67. pub segments: Vec<Segment>,
  68. }
  69. impl IfmtInput {
  70. pub fn from_str(input: &str) -> Result<Self> {
  71. let mut chars = input.chars().peekable();
  72. let mut segments = Vec::new();
  73. let mut current_literal = String::new();
  74. while let Some(c) = chars.next() {
  75. if c == '{' {
  76. if let Some(c) = chars.next_if(|c| *c == '{') {
  77. current_literal.push(c);
  78. continue;
  79. }
  80. segments.push(Segment::Literal(current_literal));
  81. current_literal = String::new();
  82. let mut current_captured = String::new();
  83. while let Some(c) = chars.next() {
  84. if c == ':' {
  85. let mut current_format_args = String::new();
  86. while let Some(c) = chars.next() {
  87. if c == '}' {
  88. segments.push(Segment::Formatted {
  89. format_args: current_format_args,
  90. segment: FormattedSegment::parse(&current_captured)?,
  91. });
  92. break;
  93. }
  94. current_format_args.push(c);
  95. }
  96. break;
  97. }
  98. if c == '}' {
  99. segments.push(Segment::Formatted {
  100. format_args: String::new(),
  101. segment: FormattedSegment::parse(&current_captured)?,
  102. });
  103. break;
  104. }
  105. current_captured.push(c);
  106. }
  107. } else {
  108. current_literal.push(c);
  109. }
  110. }
  111. segments.push(Segment::Literal(current_literal));
  112. Ok(Self { segments })
  113. }
  114. }
  115. #[derive(Debug)]
  116. pub enum Segment {
  117. Literal(String),
  118. Formatted {
  119. format_args: String,
  120. segment: FormattedSegment,
  121. },
  122. }
  123. #[derive(Debug)]
  124. pub enum FormattedSegment {
  125. Expr(Expr),
  126. Ident(Ident),
  127. }
  128. impl FormattedSegment {
  129. fn parse(input: &str) -> Result<Self> {
  130. if let Ok(ident) = parse_str::<Ident>(input) {
  131. if &ident.to_string() == input {
  132. return Ok(Self::Ident(ident));
  133. }
  134. }
  135. // if let Ok(expr) = parse_str(&("{".to_string() + input + "}")) {
  136. if let Ok(expr) = parse_str(input) {
  137. Ok(Self::Expr(expr))
  138. } else {
  139. Err(Error::new(
  140. Span::call_site(),
  141. "Expected Ident or Expression",
  142. ))
  143. }
  144. }
  145. }
  146. impl ToTokens for FormattedSegment {
  147. fn to_tokens(&self, tokens: &mut TokenStream) {
  148. match self {
  149. Self::Expr(expr) => expr.to_tokens(tokens),
  150. Self::Ident(ident) => ident.to_tokens(tokens),
  151. }
  152. }
  153. }
  154. impl Parse for IfmtInput {
  155. fn parse(input: ParseStream) -> Result<Self> {
  156. let input: LitStr = input.parse()?;
  157. let input_str = input.value();
  158. IfmtInput::from_str(&input_str)
  159. }
  160. }