1pub(crate) struct TemplDisplay;
7
8impl quote::ToTokens for TemplDisplay {
9 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
10 quote::quote! {::tour::TemplDisplay}.to_tokens(tokens);
11 }
12}
13
14pub(crate) struct TemplWrite;
16
17impl quote::ToTokens for TemplWrite {
18 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
19 quote::quote! {::tour::TemplWrite}.to_tokens(tokens);
20 }
21}
22
23pub const DERIVE_ATTRIBUTE: &str = "template";
27
28pub const INNER_BLOCK: &str = "inner";
30
31pub(crate) fn name() -> syn::Ident {
32 use std::sync::atomic::{AtomicUsize, Ordering};
33 static COUNTER: AtomicUsize = AtomicUsize::new(0);
34 let c = COUNTER.fetch_add(1, Ordering::Relaxed);
35 quote::format_ident!("TourNS{c}")
36}
37
38pub(crate) mod path {
51 use std::{path::{Path, PathBuf}, rc::Rc};
52
53 use super::error;
54 use crate::config::Config;
55
56 pub fn cwd() -> PathBuf {
57 std::env::current_dir().expect("current dir")
58 }
59
60 pub fn boxed(buf: PathBuf) -> Rc<str> {
61 buf.to_string_lossy().into()
62 }
63
64 pub fn resolve(mut path: &str, conf: &Config) -> syn::Result<Rc<str>> {
65 let mut cwd = cwd();
66 match () {
67 _ if path.starts_with(".") => error!("cannot get template file using relative path"),
68 _ if path.starts_with("/") => path = path.trim_start_matches('/'),
69 _ => cwd.push(conf.templ_dir()),
70 };
71 Ok(resolve_at(path, cwd))
72 }
73
74 pub fn resolve_at(path: impl AsRef<Path>, cwd: impl Into<PathBuf>) -> Rc<str> {
76 let path = path.as_ref();
77 let mut cwd = match () {
78 _ if path.starts_with("/") => self::cwd(),
79 _ => cwd.into(),
80 };
81 cwd.push(match path.strip_prefix("/") {
82 Ok(path) => path,
83 Err(_) => path,
84 });
85 normalize(cwd.as_path())
86 .to_string_lossy()
87 .into()
88 }
89
90 pub fn normalize(path: &Path) -> PathBuf {
94 use std::path::Component;
95 let mut components = path.components().peekable();
96 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
97 components.next();
98 PathBuf::from(c.as_os_str())
99 } else {
100 PathBuf::new()
101 };
102
103 for component in components {
104 match component {
105 Component::Prefix(..) => unreachable!(),
106 Component::RootDir => {
107 ret.push(component.as_os_str());
108 }
109 Component::CurDir => {}
110 Component::ParentDir => {
111 ret.pop();
112 }
113 Component::Normal(c) => {
114 ret.push(c);
115 }
116 }
117 }
118 ret
119 }
120}
121
122macro_rules! error {
136 (@ $s:expr, $($tt:tt)*) => {
137 return Err(syn::Error::new($s, format!($($tt)*)))
138 };
139 (dbg $($tt:tt)*) => {{
140 let me = $($tt)*;
141 panic!("{:?}", me);
142 me
143 }};
144 (.$s:expr, $($tt:tt)*) => {
145 if $s { crate::common::error!($($tt)*) }
146 };
147 (?$s:expr, $($tt:tt)*) => {
148 match $s { Some(ok) => ok, None => crate::common::error!($($tt)*), }
149 };
150 (!$s:expr, $($tt:tt)*) => {
151 match $s { Ok(ok) => ok, Err(err) => crate::common::error!(@proc_macro2::Span::call_site(), $($tt)*, err), }
152 };
153 (!$s:expr) => {
154 match $s { Ok(ok) => ok, Err(err) => crate::common::error!("{err}"), }
155 };
156 ($msg:literal, $($tt:tt)*) => {
157 crate::common::error!(@ proc_macro2::Span::call_site(), $msg, $($tt)*)
158 };
159 ($s:expr, $($tt:tt)*) => {
160 crate::common::error!(@ $s.span(), $($tt)*)
161 };
162 ($($tt:tt)*) => {
163 crate::common::error!(@ proc_macro2::Span::call_site(), $($tt)*)
164 };
165}
166
167pub(crate) use error;