use std::vec::IntoIter;
mod conversion;
mod expansions;
mod token;
pub use expansions::*;
pub use token::*;
#[derive(Debug, Clone)]
pub struct ProcMacroResult {
pub token_stream: TokenStream,
pub aux_data: Option<AuxData>,
pub diagnostics: Vec<Diagnostic>,
pub full_path_markers: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct AuxData(Vec<u8>);
impl AuxData {
pub fn new(data: Vec<u8>) -> Self {
Self(data)
}
}
impl From<&[u8]> for AuxData {
fn from(bytes: &[u8]) -> Self {
Self(bytes.to_vec())
}
}
impl From<AuxData> for Vec<u8> {
fn from(aux_data: AuxData) -> Vec<u8> {
aux_data.0
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic {
message: String,
severity: Severity,
span: Option<TextSpan>,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Severity {
Error = 1,
Warning = 2,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Diagnostics(Vec<Diagnostic>);
impl Diagnostic {
pub fn new(level: Severity, message: impl ToString) -> Self {
Self {
message: message.to_string(),
severity: level,
span: None,
}
}
pub fn spanned(span: TextSpan, level: Severity, message: impl ToString) -> Self {
let span = if span.start > span.end {
TextSpan::new(span.end, span.start)
} else {
span
};
Self {
message: message.to_string(),
severity: level,
span: Some(span),
}
}
pub fn error(message: impl ToString) -> Self {
Self {
message: message.to_string(),
severity: Severity::Error,
span: None,
}
}
pub fn warn(message: impl ToString) -> Self {
Self {
message: message.to_string(),
severity: Severity::Warning,
span: None,
}
}
pub fn span_error(span: TextSpan, message: impl ToString) -> Self {
Self::spanned(span, Severity::Error, message)
}
pub fn span_warning(span: TextSpan, message: impl ToString) -> Self {
Self::spanned(span, Severity::Warning, message)
}
pub fn span(&self) -> Option<TextSpan> {
self.span.clone()
}
pub fn message(&self) -> &str {
&self.message
}
pub fn severity(&self) -> Severity {
self.severity
}
}
impl From<Vec<Diagnostic>> for Diagnostics {
fn from(diagnostics: Vec<Diagnostic>) -> Self {
Self(diagnostics)
}
}
impl From<Diagnostic> for Diagnostics {
fn from(diagnostics: Diagnostic) -> Self {
Self(vec![diagnostics])
}
}
impl Diagnostics {
pub fn new(diagnostics: Vec<Diagnostic>) -> Self {
Self(diagnostics)
}
pub fn error(mut self, message: impl ToString) -> Self {
self.0.push(Diagnostic::error(message));
self
}
pub fn warn(mut self, message: impl ToString) -> Self {
self.0.push(Diagnostic::warn(message));
self
}
pub fn span_error(mut self, span: TextSpan, message: impl ToString) -> Self {
self.0.push(Diagnostic::span_error(span, message));
self
}
pub fn span_warning(mut self, span: TextSpan, message: impl ToString) -> Self {
self.0.push(Diagnostic::span_warning(span, message));
self
}
}
impl IntoIterator for Diagnostics {
type Item = Diagnostic;
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> IntoIter<Diagnostic> {
self.0.into_iter()
}
}
impl FromIterator<Diagnostic> for Diagnostics {
fn from_iter<T: IntoIterator<Item = Diagnostic>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl Extend<Diagnostic> for Diagnostics {
fn extend<T: IntoIterator<Item = Diagnostic>>(&mut self, iter: T) {
self.0.extend(iter);
}
}
impl ProcMacroResult {
pub fn new(token_stream: TokenStream) -> Self {
Self {
token_stream,
aux_data: Default::default(),
diagnostics: Default::default(),
full_path_markers: Default::default(),
}
}
pub fn with_aux_data(mut self, aux_data: AuxData) -> Self {
self.aux_data = Some(aux_data);
self
}
pub fn with_full_path_markers(mut self, full_path_markers: Vec<String>) -> Self {
self.full_path_markers.extend(full_path_markers);
self
}
pub fn with_diagnostics(mut self, diagnostics: Diagnostics) -> Self {
self.diagnostics.extend(diagnostics);
self
}
}
#[derive(Clone, Debug)]
pub struct PostProcessContext {
pub aux_data: Vec<AuxData>,
pub full_path_markers: Vec<FullPathMarker>,
}
#[derive(Clone, Debug)]
pub struct FullPathMarker {
pub key: String,
pub full_path: String,
}
#[cfg(test)]
mod tests {
use crate::types::TokenStream;
use crate::{AllocationContext, TextSpan, Token, TokenTree};
#[test]
fn new_token_stream_metadata_empty() {
let token_stream = TokenStream::empty();
assert!(token_stream.metadata.file_id.is_none());
assert!(token_stream.metadata.original_file_path.is_none());
}
#[test]
fn can_convert_to_stable() {
let token_stream = TokenStream::new(vec![
TokenTree::Ident(Token::new("test", TextSpan::new(0, 4))),
TokenTree::Ident(Token::new(";", TextSpan::new(4, 5))),
]);
let stable = token_stream.as_stable();
let ctx = AllocationContext::default();
let token_stream = unsafe { TokenStream::from_stable_in(&stable, &ctx) };
assert_eq!(token_stream.tokens.len(), 2);
assert_eq!(token_stream.to_string(), "test;");
}
#[test]
fn can_store_null_character() {
let token_stream = TokenStream::new(vec![TokenTree::Ident(Token::new(
"te\0st",
TextSpan::new(0, 4),
))]);
let stable = token_stream.as_stable();
let ctx = AllocationContext::default();
let token_stream = unsafe { TokenStream::from_stable_in(&stable, &ctx) };
assert_eq!(token_stream.tokens.len(), 1);
assert_eq!(token_stream.to_string(), "te\0st");
}
}