some fixes of my macros

Dieser Commit ist enthalten in:
Sebastian Tobie 2024-12-03 09:18:18 +01:00
Ursprung efa52f4f5b
Commit 587ef24505
2 geänderte Dateien mit 108 neuen und 89 gelöschten Zeilen

Datei anzeigen

@ -8,5 +8,8 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1.0.*" quote = "1.0.*"
syn = "2.0.*" proc-macro2 = {version = "*", features = ["span-locations"]}
proc-macro2 = {version = "*", features = ["span-locations"]}
[dependencies.syn]
version = ">=2.0.90"
features = ["full"]

Datei anzeigen

@ -1,113 +1,107 @@
use std::fs; use std::fs;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::{format_ident, quote};
use syn::{ use syn::{
buffer::Cursor, buffer::Cursor,
parse::{Parse, ParseStream, StepCursor}, parse::{Parse, ParseStream, StepCursor},
Error, LitStr, spanned::Spanned,
Error, Ident, Lit, LitInt,
}; };
use syn::{parse, LitInt, Result}; use syn::{parse, Result};
const DATA_DIR: &str = "data"; const DATA_DIR: &str = "data";
const EXAMPLE_DIR: &str = "examples"; const EXAMPLE_DIR: &str = "examples";
const VAR_NAME: &str = "DATA";
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct IncludeData { struct IncludeData {
var: String, var: String,
year: String, year: Option<String>,
day: String, day: Option<String>,
} }
fn get_string<'a, 'b>(cursor: StepCursor<'a, 'b>) -> Result<(String, Cursor<'a>)> { impl Default for IncludeData {
let (lit, c) = match cursor.literal() { fn default() -> Self {
Some(literal) => literal, IncludeData {
None => return Err(Error::new(cursor.span(), "Failed to get literal")), var: VAR_NAME.into(),
}; year: None,
let span = lit.span(); day: None,
let text = match span.source_text() { }
Some(text) => text, }
}
fn get_text<T: Spanned>(item: T) -> Result<String> {
let span = item.span();
match span.source_text() {
Some(text) => Ok(text),
None => { None => {
let start = span.start(); let start = span.start();
let end = span.end(); let end = span.end();
return Err(Error::new( Err(Error::new(
span, span,
format!( format!(
"Failed to get sourcetext for {}:{}-{}:{}", "Failed to get sourcetext for {}:{}-{}:{}",
start.line, start.column, end.line, end.column, start.line, start.column, end.line, end.column,
), ),
)); ))
} }
}
}
fn get_literal<'a, 'b>(cursor: StepCursor<'a, 'b>) -> Result<(String, Cursor<'a>)> {
let (lit, c) = match cursor.literal() {
Some(literal) => literal,
None => return Err(Error::new(cursor.span(), "Failed to get literal")),
}; };
let text = get_text(lit)?;
Ok((text, c))
}
fn get_ident<'a, 'b>(cursor: StepCursor<'a, 'b>) -> Result<(String, Cursor<'a>)> {
let (lit, c) = match cursor.ident() {
Some(literal) => literal,
None => return Err(Error::new(cursor.span(), "Failed to get literal")),
};
let text = get_text(lit)?;
Ok((text, c)) Ok((text, c))
} }
impl Parse for IncludeData { impl Parse for IncludeData {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let mut data = IncludeData { let mut data = IncludeData::default();
var: "".into(), if input.peek(Ident) && !input.peek(LitInt) {
year: "".into(), data.var = get_text(input.parse::<Ident>().unwrap())?;
day: "".into(),
};
let mut look = input.lookahead1();
if !look.peek(LitStr) {
let s = input.span();
return Err(Error::new(
s,
format!("{} is not an valid Token", s.source_text().unwrap()),
));
} }
data.var = input.step(get_string).expect("Wanted a string"); data.year = Some(get_text(input.parse::<LitInt>()?)?);
look = input.lookahead1(); data.day = Some(get_text(input.parse::<Lit>()?)?);
if !look.peek(LitInt) {
let s = input.span();
return Err(Error::new(
s,
format!("{} is not an valid Year", s.source_text().unwrap()),
));
}
data.year = input.step(get_string).expect("Wanted an string");
look = input.lookahead1();
if !look.peek(LitInt) {
let s = input.span();
return Err(Error::new(
s,
format!("{} is not an valid Day", s.source_text().unwrap()),
));
}
data.day = input.step(get_string).expect("Wanted an string");
Ok(data) Ok(data)
} }
} }
fn canonicalize(dir: &str, input: Result<IncludeData>) -> Option<String> { fn canonicalize(dir: &str, input: IncludeData) -> Option<String> {
let pathname = match input { let pathname = format!("{}/{}/{}.txt", dir, input.year.unwrap(), input.day.unwrap());
Ok(id) => Some(format!("{}/{}/{}.txt", dir, id.year, id.day)), match fs::canonicalize(pathname) {
Err(_) => None, Ok(canon) => {
}; if canon.is_file() {
if let Some(p) = pathname { match canon.to_str() {
match fs::canonicalize(p.clone()) { Some(c) => Some(c.to_string()),
Ok(canon) => { None => None,
if canon.is_file() {
Some(canon.to_str().unwrap().to_string())
} else {
println!("ERROR: {:?} is not a file", canon);
None
} }
} } else {
Err(err) => {
println!("ERROR: {} does not exist: {}", p, err);
None None
} }
} }
} else { Err(_) => None,
None
} }
} }
/// includes Data from Advent of code /// includes Data from Advent of code
/// ```ignore (cannot-doctest-external-file-dependency) /// ```ignore (cannot-doctest-external-file-dependency)
/// include_data!(DATA 2015 01) /// include_data!(DATA 2015 01)
/// // or
/// include_data!(2015 01)
/// ///
/// fn main() { /// fn main() {
/// print!("{DATA}"); /// print!("{DATA}");
@ -119,37 +113,59 @@ fn canonicalize(dir: &str, input: Result<IncludeData>) -> Option<String> {
/// ``` /// ```
#[proc_macro] #[proc_macro]
pub fn include_data(data: TokenStream) -> TokenStream { pub fn include_data(data: TokenStream) -> TokenStream {
let input = parse::<IncludeData>(data); let input = match parse::<IncludeData>(data) {
let name = match input.clone() { Ok(include) => include,
Ok(d) => d.var, Err(error) => return error.into_compile_error().into(),
Err(_)=>"DATA".into(),
}; };
let mut path = canonicalize(DATA_DIR, input.clone()); let ident = format_ident!("{}", input.var);
if path.is_none() { let mut comment: String;
path = canonicalize(EXAMPLE_DIR, input) if input.day.is_some() && input.year.is_some() {
} comment = "Data from the data dir".into();
match path { let mut path = canonicalize(DATA_DIR, input.clone());
Some(p) => quote! { if path.is_none() {
static #name: &str = include_str!(#p); comment = "Data from the examples".into();
path = canonicalize(EXAMPLE_DIR, input)
} }
.into(), match path {
None => quote! { Some(p) => {
static #name: &str = ""; return quote! {
#[doc = #comment]
static #ident: &str = include_str!(#p);
}
.into()
}
None => comment = "failed to get data from the paths".into(),
} }
.into(), } else {
comment = format!(
"Failed to get the year({:?}) or day({:?})",
input.year, input.day
);
} }
quote! {
#[doc = #comment]
static #ident: &str = "";
}
.into()
} }
#[proc_macro] #[proc_macro]
pub fn include_example(data: TokenStream) -> TokenStream { pub fn include_example(data: TokenStream) -> TokenStream {
match canonicalize(EXAMPLE_DIR, parse::<IncludeData>(data)) { let input = parse::<IncludeData>(data).unwrap_or_default();
Some(p) => quote! { let ident = format_ident!("{}", input.var);
static DATA: &str = include_str!(#p); if input.day.is_some() && input.year.is_some() {
match canonicalize(EXAMPLE_DIR, input.clone()) {
Some(p) => {
return quote! {
static #ident: &str = include_str!(#p);
}
.into()
}
None => {}
} }
.into(),
None => quote! {
static DATA: &str = "";
}
.into(),
} }
quote! {
static #ident: &str = "";
}
.into()
} }