use std::sync::OnceLock;
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::quote;
const CORE_PKG: &str = "toolu-orm-core";
const QUERY_PKG: &str = "toolu-orm-query";
const FACADE_PKG: &str = "toolu-orm";
struct CratePath {
root: Root,
module: Option<&'static str>,
}
enum Root {
Itself,
Extern(String),
}
impl CratePath {
fn tokens(&self) -> TokenStream {
let base = match &self.root {
Root::Itself => quote! { crate },
Root::Extern(name) => {
let ident = Ident::new(name, Span::call_site());
quote! { ::#ident }
},
};
match self.module {
Some(module) => {
let ident = Ident::new(module, Span::call_site());
quote! { #base::#ident }
},
None => base,
}
}
fn to_literal(&self) -> String {
let base = match &self.root {
Root::Itself => "crate".to_owned(),
Root::Extern(name) => format!("::{name}"),
};
match self.module {
Some(module) => format!("{base}::{module}"),
None => base,
}
}
}
fn resolve(direct: &str, module: &'static str) -> CratePath {
if let Some(root) = found(direct) {
return CratePath { root, module: None };
}
if let Some(root) = found(FACADE_PKG) {
return CratePath {
root,
module: Some(module),
};
}
CratePath {
root: Root::Extern(sanitize(direct)),
module: None,
}
}
fn found(package: &str) -> Option<Root> {
match crate_name(package) {
Ok(FoundCrate::Itself) => Some(Root::Itself),
Ok(FoundCrate::Name(name)) => Some(Root::Extern(sanitize(&name))),
Err(_) => None,
}
}
fn sanitize(name: &str) -> String {
name.replace('-', "_")
}
fn core_path() -> &'static CratePath {
static CORE: OnceLock<CratePath> = OnceLock::new();
CORE.get_or_init(|| resolve(CORE_PKG, "core"))
}
pub fn core() -> TokenStream {
core_path().tokens()
}
pub fn query() -> TokenStream {
static QUERY: OnceLock<CratePath> = OnceLock::new();
QUERY.get_or_init(|| resolve(QUERY_PKG, "query")).tokens()
}
pub fn core_serde_literal() -> String {
format!("{}::serde", core_path().to_literal())
}