use crate::{
PointTrack,
exprs::literal::ExprLit,
trees::{
group::stream_stringify_with_fns,
loader::{LoadFileAndAutoMakeTreeErr, load_file_and_automake_tree_with_fns},
null::make_null_group,
result::TreeResult,
},
};
use proc_macro2::{
Delimiter, Group, Literal, Span, TokenStream as TokenStream2, TokenTree as TokenTree2,
};
use quote::ToTokens;
use std::{borrow::Cow, io::Error as IOError, path::Path};
use std::{fs::File, io::Read};
pub trait BehMacroInclude {
type Result;
fn make_tree(
arg0: &ExprLit,
point_track_file: Option<&mut PointTrack>,
span: Span,
) -> TreeResult<Self::Result>;
fn make_empty_tree(group_span: Span) -> Self::Result;
}
pub enum InjectTT {}
impl BehMacroInclude for InjectTT {
type Result = TokenTree2;
#[inline]
fn make_empty_tree(group_span: Span) -> Self::Result {
make_null_group(group_span)
}
fn make_tree(
sspath: &ExprLit,
point_track: Option<&mut PointTrack>,
span: Span,
) -> TreeResult<Self::Result> {
let path = Path::new(sspath.as_str());
load_file_and_automake_tree_with_fns(
path,
point_track,
|_| {},
|fs_tt| {
let ett = fs_tt.map_or_else(TokenStream2::new, TokenStream2::from_iter);
let mut ngroup = Group::new(Delimiter::None, ett);
ngroup.set_span(span);
TreeResult::Ok(TokenTree2::Group(ngroup))
},
|e| TreeResult::Err(e.into_tt_err(span)),
)
}
}
pub enum InjectCTT {}
impl BehMacroInclude for InjectCTT {
type Result = TokenTree2;
#[inline]
fn make_empty_tree(group_span: Span) -> Self::Result {
make_null_group(group_span)
}
fn make_tree(
sspath: &ExprLit,
point_track: Option<&mut PointTrack>,
span: Span,
) -> TreeResult<Self::Result> {
let sspath = Path::new(sspath);
load_file_and_automake_tree_with_fns(
sspath,
point_track,
|p_string| {
let mut p_str = p_string.as_mut();
while let Some(pos) = p_str.find('\\' ) {
let right = unsafe {
debug_assert!(p_str.get(pos..).is_some());
p_str.get_unchecked_mut(pos..)
};
if right.len() >= 2 {
let (c_symbol, new_pstr) = right.split_at_mut(2);
let c_array = unsafe { c_symbol.as_bytes_mut() };
debug_assert_eq!(c_array.len(), 2);
debug_assert_eq!(
{
#[allow(clippy::get_first)]
c_array.get(0)
},
Some(&b'\\')
);
debug_assert!(c_array.get(1).is_some());
match unsafe { c_array.get_unchecked(1) } {
b'\n' | b'\t' | b'\r' | b' ' => {
let a_repl = unsafe { c_array.get_unchecked_mut(0) };
*a_repl = b' ';
}
_ => {}
}
if new_pstr.is_empty() {
break;
}
p_str = new_pstr;
} else {
let array = unsafe { right.as_bytes_mut() }.iter_mut();
for a in array {
*a = b' ';
}
break;
}
}
},
|fs_tt| {
let ett = fs_tt.map_or_else(TokenStream2::new, TokenStream2::from_iter);
let mut ngroup = Group::new(Delimiter::None, ett);
ngroup.set_span(span);
TreeResult::Ok(TokenTree2::Group(ngroup))
},
|e| TreeResult::Err(e.into_tt_err(span)),
)
}
}
pub enum InjectStr {}
impl BehMacroInclude for InjectStr {
type Result = TokenTree2;
fn make_empty_tree(group_span: Span) -> Self::Result {
let mut lit = Literal::string("");
lit.set_span(group_span);
TokenTree2::Literal(lit)
}
fn make_tree(
sspath: &ExprLit,
point_track: Option<&mut PointTrack>,
span: Span,
) -> TreeResult<Self::Result> {
let path = Path::new(sspath);
match std::fs::read_to_string(path) {
Ok(data) => {
if let Some(point_track) = point_track {
point_track.append_track_file(path);
}
let mut lit = Literal::string(&data);
lit.set_span(span);
TreeResult::Ok(TokenTree2::Literal(lit))
}
Err(e) => {
let path = path
.canonicalize()
.map_or_else(|_| Cow::Borrowed(path), Cow::Owned);
TreeResult::Err(
LoadFileAndAutoMakeTreeErr::read_to_string(e, path).into_tt_err(span),
)
}
}
}
}
pub enum InjectArr {}
impl BehMacroInclude for InjectArr {
type Result = TokenTree2;
fn make_empty_tree(group_span: Span) -> Self::Result {
let mut lit = Literal::byte_string(&[]);
lit.set_span(group_span);
TokenTree2::Literal(lit)
}
fn make_tree(
sspath: &ExprLit,
point_track: Option<&mut PointTrack>,
span: Span,
) -> TreeResult<Self::Result> {
let path = Path::new(sspath);
let vec = {
let make_err = |e: IOError| {
let path = path
.canonicalize()
.map_or_else(|_| Cow::Borrowed(path), Cow::Owned);
TreeResult::from(
LoadFileAndAutoMakeTreeErr::read_to_string(e, path).into_tt_err(span),
)
};
let mut file = match File::open(path) {
Ok(a) => a,
Err(e) => return make_err(e),
};
let mut vec = Vec::new(); if let Err(e) = file.read_to_end(&mut vec) {
return make_err(e);
};
vec
};
if let Some(point_track) = point_track {
point_track.append_track_file(path);
}
let mut lit = Literal::byte_string(&vec);
lit.set_span(span);
TreeResult::Ok(TokenTree2::Literal(lit))
}
}
pub fn macro_rule_include<A>(
group: &'_ Group,
point_track: Option<&mut PointTrack>,
) -> TreeResult<A::Result>
where
A: BehMacroInclude,
{
let span = group.span();
let stream = group.into_token_stream();
stream_stringify_with_fns(
stream,
|stringify| {
let exprlit = unsafe { ExprLit::new_unchecked(&stringify) };
A::make_tree(exprlit, point_track, span)
},
|| TreeResult::Ok(A::make_empty_tree(span)),
TreeResult::Err,
)
}