use core::ops::Deref;
use proc_macro2::Span;
use quote::{quote_spanned, ToTokens};
mod test;
pub struct Warning {
pub name: String,
pub index: Option<usize>,
pub message: String,
pub links: Vec<String>,
pub span: Span,
}
#[derive(Default)]
pub struct DeprecatedWarningBuilder {
title: String,
index: Option<usize>,
old: Option<String>,
new: Option<String>,
links: Vec<String>,
span: Option<Span>,
}
impl DeprecatedWarningBuilder {
#[must_use]
pub fn from_title(title: &str) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { title: title.into(), ..Default::default() }
}
#[must_use]
pub fn index(self, index: usize) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { index: Some(index), ..self }
}
#[must_use]
pub fn old(self, old: &str) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { old: Some(old.into()), ..self }
}
#[must_use]
pub fn new(self, new: &str) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { new: Some(new.into()), ..self }
}
#[must_use]
pub fn help_link(self, link: &str) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { links: vec![link.into()], ..self }
}
#[must_use]
pub fn help_links(self, links: &[&str]) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { links: links.iter().map(|s| s.deref().into()).collect(), ..self }
}
#[must_use]
pub fn span(self, span: Span) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder { span: Some(span), ..self }
}
pub fn maybe_build(self) -> Result<Warning, String> {
let span = self.span.unwrap_or_else(Span::call_site);
let title = self.title;
let old = self.old.ok_or("Missing old")?;
let new = self.new.ok_or("Missing new")?;
let message = format!("It is deprecated to {}.\nPlease instead {}.", old, new);
Ok(Warning { name: title, index: self.index, message, links: self.links, span })
}
#[must_use]
pub fn build(self) -> Warning {
self.maybe_build().expect("maybe_build failed")
}
}
impl Warning {
pub fn new_raw(
name: String,
index: Option<usize>,
message: String,
help_links: Vec<String>,
span: Span,
) -> Warning {
Warning { name, index, message, links: help_links, span }
}
#[must_use]
pub fn new_deprecated(title: &str) -> DeprecatedWarningBuilder {
DeprecatedWarningBuilder::from_title(title)
}
fn final_message(&self) -> String {
let lines = self.message.trim().lines().map(|line| line.trim_start());
let message = lines.map(|line| format!("\t\t{}", line)).collect::<Vec<_>>().join("\n");
if !self.links.is_empty() {
let link = self
.links
.iter()
.map(|l| format!("<{}>", l))
.collect::<Vec<_>>()
.join("\n\t\t\t");
format!("\n{}\n\n\t\tFor more info see:\n\t\t\t{}", message, link)
} else {
format!("\n{}", message)
}
}
fn final_name(&self) -> syn::Ident {
let name = match self.index {
Some(i) => format!("{}_{}", self.name, i),
None => self.name.clone(),
};
syn::Ident::new(&name, self.span)
}
}
impl ToTokens for Warning {
fn to_tokens(&self, stream: &mut proc_macro2::TokenStream) {
let name = self.final_name();
let message = self.final_message();
let q = quote_spanned!(self.span =>
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
fn #name() {
#[deprecated(note = #message)]
#[allow(non_upper_case_globals)]
const _w: () = ();
let _ = _w;
}
);
q.to_tokens(stream);
}
}