ifmt.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. use std::str::FromStr;
  2. use proc_macro2::{Span, TokenStream};
  3. use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
  4. use syn::{
  5. parse::{Parse, ParseStream},
  6. *,
  7. };
  8. pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
  9. Ok(input.into_token_stream())
  10. }
  11. #[allow(dead_code)] // dumb compiler does not see the struct being used...
  12. #[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
  13. pub struct IfmtInput {
  14. pub source: Option<LitStr>,
  15. pub segments: Vec<Segment>,
  16. }
  17. impl IfmtInput {
  18. pub fn new_static(input: &str) -> Self {
  19. Self {
  20. source: None,
  21. segments: vec![Segment::Literal(input.to_string())],
  22. }
  23. }
  24. pub fn join(mut self, other: Self, separator: &str) -> Self {
  25. if !self.segments.is_empty() {
  26. self.segments.push(Segment::Literal(separator.to_string()));
  27. }
  28. self.segments.extend(other.segments);
  29. if let Some(source) = &other.source {
  30. self.source = Some(LitStr::new(
  31. &format!(
  32. "{}{}{}",
  33. self.source.as_ref().unwrap().value(),
  34. separator,
  35. source.value()
  36. ),
  37. source.span(),
  38. ));
  39. }
  40. self
  41. }
  42. pub fn push_expr(&mut self, expr: Expr) {
  43. self.segments.push(Segment::Formatted(FormattedSegment {
  44. format_args: String::new(),
  45. segment: FormattedSegmentType::Expr(Box::new(expr)),
  46. }));
  47. }
  48. pub fn push_str(&mut self, s: &str) {
  49. self.segments.push(Segment::Literal(s.to_string()));
  50. if let Some(source) = &self.source {
  51. self.source = Some(LitStr::new(
  52. &format!("{}{}", source.value(), s),
  53. source.span(),
  54. ));
  55. }
  56. }
  57. pub fn is_static(&self) -> bool {
  58. self.segments
  59. .iter()
  60. .all(|seg| matches!(seg, Segment::Literal(_)))
  61. }
  62. }
  63. impl IfmtInput {
  64. pub fn to_static(&self) -> Option<String> {
  65. self.segments
  66. .iter()
  67. .try_fold(String::new(), |acc, segment| {
  68. if let Segment::Literal(seg) = segment {
  69. Some(acc + seg)
  70. } else {
  71. None
  72. }
  73. })
  74. }
  75. fn is_simple_expr(&self) -> bool {
  76. self.segments.iter().all(|seg| match seg {
  77. Segment::Literal(_) => true,
  78. Segment::Formatted(FormattedSegment { segment, .. }) => {
  79. matches!(segment, FormattedSegmentType::Ident(_))
  80. }
  81. })
  82. }
  83. /// Try to convert this into a single _.to_string() call if possible
  84. ///
  85. /// Using "{single_expression}" is pretty common, but you don't need to go through the whole format! machinery for that, so we optimize it here.
  86. fn try_to_string(&self) -> Option<TokenStream> {
  87. let mut single_dynamic = None;
  88. for segment in &self.segments {
  89. match segment {
  90. Segment::Literal(literal) => {
  91. if !literal.is_empty() {
  92. return None;
  93. }
  94. }
  95. Segment::Formatted(FormattedSegment {
  96. segment,
  97. format_args,
  98. }) => {
  99. if format_args.is_empty() {
  100. match single_dynamic {
  101. Some(current_string) => {
  102. single_dynamic =
  103. Some(quote!(#current_string + &(#segment).to_string()));
  104. }
  105. None => {
  106. single_dynamic = Some(quote!((#segment).to_string()));
  107. }
  108. }
  109. } else {
  110. return None;
  111. }
  112. }
  113. }
  114. }
  115. single_dynamic
  116. }
  117. }
  118. impl FromStr for IfmtInput {
  119. type Err = syn::Error;
  120. fn from_str(input: &str) -> Result<Self> {
  121. let mut chars = input.chars().peekable();
  122. let mut segments = Vec::new();
  123. let mut current_literal = String::new();
  124. while let Some(c) = chars.next() {
  125. if c == '{' {
  126. if let Some(c) = chars.next_if(|c| *c == '{') {
  127. current_literal.push(c);
  128. continue;
  129. }
  130. if !current_literal.is_empty() {
  131. segments.push(Segment::Literal(current_literal));
  132. }
  133. current_literal = String::new();
  134. let mut current_captured = String::new();
  135. while let Some(c) = chars.next() {
  136. if c == ':' {
  137. // two :s in a row is a path, not a format arg
  138. if chars.next_if(|c| *c == ':').is_some() {
  139. current_captured.push_str("::");
  140. continue;
  141. }
  142. let mut current_format_args = String::new();
  143. for c in chars.by_ref() {
  144. if c == '}' {
  145. segments.push(Segment::Formatted(FormattedSegment {
  146. format_args: current_format_args,
  147. segment: FormattedSegmentType::parse(&current_captured)?,
  148. }));
  149. break;
  150. }
  151. current_format_args.push(c);
  152. }
  153. break;
  154. }
  155. if c == '}' {
  156. segments.push(Segment::Formatted(FormattedSegment {
  157. format_args: String::new(),
  158. segment: FormattedSegmentType::parse(&current_captured)?,
  159. }));
  160. break;
  161. }
  162. current_captured.push(c);
  163. }
  164. } else {
  165. if '}' == c {
  166. if let Some(c) = chars.next_if(|c| *c == '}') {
  167. current_literal.push(c);
  168. continue;
  169. } else {
  170. return Err(Error::new(
  171. Span::call_site(),
  172. "unmatched closing '}' in format string",
  173. ));
  174. }
  175. }
  176. current_literal.push(c);
  177. }
  178. }
  179. if !current_literal.is_empty() {
  180. segments.push(Segment::Literal(current_literal));
  181. }
  182. Ok(Self {
  183. segments,
  184. source: None,
  185. })
  186. }
  187. }
  188. impl ToTokens for IfmtInput {
  189. fn to_tokens(&self, tokens: &mut TokenStream) {
  190. // Try to turn it into a single _.to_string() call
  191. if !cfg!(debug_assertions) {
  192. if let Some(single_dynamic) = self.try_to_string() {
  193. tokens.extend(single_dynamic);
  194. return;
  195. }
  196. }
  197. // If the segments are not complex exprs, we can just use format! directly to take advantage of RA rename/expansion
  198. if self.is_simple_expr() {
  199. let raw = &self.source;
  200. tokens.extend(quote! {
  201. ::std::format_args!(#raw)
  202. });
  203. return;
  204. }
  205. // build format_literal
  206. let mut format_literal = String::new();
  207. let mut expr_counter = 0;
  208. for segment in self.segments.iter() {
  209. match segment {
  210. Segment::Literal(s) => format_literal += &s.replace('{', "{{").replace('}', "}}"),
  211. Segment::Formatted(FormattedSegment { format_args, .. }) => {
  212. format_literal += "{";
  213. format_literal += &expr_counter.to_string();
  214. expr_counter += 1;
  215. format_literal += ":";
  216. format_literal += format_args;
  217. format_literal += "}";
  218. }
  219. }
  220. }
  221. let span = match self.source.as_ref() {
  222. Some(source) => source.span(),
  223. None => Span::call_site(),
  224. };
  225. let positional_args = self.segments.iter().filter_map(|seg| {
  226. if let Segment::Formatted(FormattedSegment { segment, .. }) = seg {
  227. let mut segment = segment.clone();
  228. // We set the span of the ident here, so that we can use it in diagnostics
  229. if let FormattedSegmentType::Ident(ident) = &mut segment {
  230. ident.set_span(span);
  231. }
  232. Some(segment)
  233. } else {
  234. None
  235. }
  236. });
  237. quote_spanned! {
  238. span =>
  239. ::std::format_args!(
  240. #format_literal
  241. #(, #positional_args)*
  242. )
  243. }
  244. .to_tokens(tokens)
  245. }
  246. }
  247. #[derive(Debug, PartialEq, Eq, Clone, Hash)]
  248. pub enum Segment {
  249. Literal(String),
  250. Formatted(FormattedSegment),
  251. }
  252. #[derive(Debug, PartialEq, Eq, Clone, Hash)]
  253. pub struct FormattedSegment {
  254. format_args: String,
  255. segment: FormattedSegmentType,
  256. }
  257. impl ToTokens for FormattedSegment {
  258. fn to_tokens(&self, tokens: &mut TokenStream) {
  259. let (fmt, seg) = (&self.format_args, &self.segment);
  260. let fmt = format!("{{0:{fmt}}}");
  261. tokens.append_all(quote! {
  262. format_args!(#fmt, #seg)
  263. });
  264. }
  265. }
  266. #[derive(Debug, PartialEq, Eq, Clone, Hash)]
  267. pub enum FormattedSegmentType {
  268. Expr(Box<Expr>),
  269. Ident(Ident),
  270. }
  271. impl FormattedSegmentType {
  272. fn parse(input: &str) -> Result<Self> {
  273. if let Ok(ident) = parse_str::<Ident>(input) {
  274. if ident == input {
  275. return Ok(Self::Ident(ident));
  276. }
  277. }
  278. if let Ok(expr) = parse_str(input) {
  279. Ok(Self::Expr(Box::new(expr)))
  280. } else {
  281. Err(Error::new(
  282. Span::call_site(),
  283. "Expected Ident or Expression",
  284. ))
  285. }
  286. }
  287. }
  288. impl ToTokens for FormattedSegmentType {
  289. fn to_tokens(&self, tokens: &mut TokenStream) {
  290. match self {
  291. Self::Expr(expr) => expr.to_tokens(tokens),
  292. Self::Ident(ident) => ident.to_tokens(tokens),
  293. }
  294. }
  295. }
  296. impl Parse for IfmtInput {
  297. fn parse(input: ParseStream) -> Result<Self> {
  298. let input: LitStr = input.parse()?;
  299. let input_str = input.value();
  300. let mut ifmt = IfmtInput::from_str(&input_str)?;
  301. ifmt.source = Some(input);
  302. Ok(ifmt)
  303. }
  304. }