mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-02 09:46:06 +00:00
WIP
This commit is contained in:
parent
2cfb75837b
commit
a1651c89d9
5 changed files with 67 additions and 182 deletions
|
@ -1,11 +1,11 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use syn::{parse::Nothing, ItemStruct};
|
||||
use syn::parse::Nothing;
|
||||
|
||||
use crate::hygiene::Hygiene;
|
||||
|
||||
// Source:
|
||||
//
|
||||
// #[salsa::db(Jar0, Jar1, Jar2)]
|
||||
// #[salsa::db]
|
||||
// pub struct Database {
|
||||
// storage: salsa::Storage<Self>,
|
||||
// }
|
||||
|
@ -15,11 +15,10 @@ pub(crate) fn db(
|
|||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let _nothing = syn::parse_macro_input!(args as Nothing);
|
||||
let db_macro = DbMacro {
|
||||
hygiene: Hygiene::from(&input),
|
||||
input: syn::parse_macro_input!(input as syn::ItemStruct),
|
||||
};
|
||||
match db_macro.try_db() {
|
||||
let hygiene = Hygiene::from(&input);
|
||||
let input = syn::parse_macro_input!(input as syn::Item);
|
||||
let db_macro = DbMacro { hygiene };
|
||||
match db_macro.try_db(input) {
|
||||
Ok(v) => v.into(),
|
||||
Err(e) => e.to_compile_error().into(),
|
||||
}
|
||||
|
@ -27,22 +26,43 @@ pub(crate) fn db(
|
|||
|
||||
struct DbMacro {
|
||||
hygiene: Hygiene,
|
||||
input: ItemStruct,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl DbMacro {
|
||||
fn try_db(self) -> syn::Result<TokenStream> {
|
||||
let has_storage_impl = self.has_storage_impl()?;
|
||||
let input = self.input;
|
||||
Ok(quote! {
|
||||
#has_storage_impl
|
||||
#input
|
||||
})
|
||||
fn try_db(self, input: syn::Item) -> syn::Result<TokenStream> {
|
||||
match input {
|
||||
syn::Item::Struct(input) => {
|
||||
let has_storage_impl = self.has_storage_impl(&input)?;
|
||||
Ok(quote! {
|
||||
#has_storage_impl
|
||||
#input
|
||||
})
|
||||
}
|
||||
syn::Item::Trait(mut input) => {
|
||||
self.add_salsa_view_method(&mut input)?;
|
||||
Ok(quote! {
|
||||
#input
|
||||
})
|
||||
}
|
||||
syn::Item::Impl(mut input) => {
|
||||
self.add_salsa_view_method_impl(&mut input)?;
|
||||
Ok(quote! {
|
||||
#input
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
input,
|
||||
"`db` must be applied to a struct, trait, or impl",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_storage_field(&self) -> syn::Result<syn::Ident> {
|
||||
fn find_storage_field(&self, input: &syn::ItemStruct) -> syn::Result<syn::Ident> {
|
||||
let storage = "storage";
|
||||
for field in self.input.fields.iter() {
|
||||
for field in input.fields.iter() {
|
||||
if let Some(i) = &field.ident {
|
||||
if i == storage {
|
||||
return Ok(i.clone());
|
||||
|
@ -56,15 +76,14 @@ impl DbMacro {
|
|||
}
|
||||
|
||||
return Err(syn::Error::new_spanned(
|
||||
&self.input.ident,
|
||||
&input.ident,
|
||||
"database struct must be a braced struct (`{}`) with a field named `storage`",
|
||||
));
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn has_storage_impl(&self) -> syn::Result<TokenStream> {
|
||||
let storage = self.find_storage_field()?;
|
||||
let db = &self.input.ident;
|
||||
fn has_storage_impl(&self, input: &syn::ItemStruct) -> syn::Result<TokenStream> {
|
||||
let storage = self.find_storage_field(input)?;
|
||||
let db = &input.ident;
|
||||
|
||||
let SalsaHasStorage = self.hygiene.ident("SalsaHasStorage");
|
||||
let SalsaStorage = self.hygiene.ident("SalsaStorage");
|
||||
|
@ -86,4 +105,26 @@ impl DbMacro {
|
|||
};
|
||||
})
|
||||
}
|
||||
|
||||
fn add_salsa_view_method(&self, input: &mut syn::ItemTrait) -> syn::Result<()> {
|
||||
input.items.push(parse_quote! {
|
||||
fn __salsa_add_view__(&self);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_salsa_view_method_impl(&self, input: &mut syn::ItemImpl) -> syn::Result<()> {
|
||||
let Some((_, TraitPath, _)) = &input.trait_ else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
&input.self_ty,
|
||||
"impl must be on a trait",
|
||||
));
|
||||
};
|
||||
input.items.push(parse_quote! {
|
||||
fn __salsa_add_view__(&self) {
|
||||
salsa::storage::views(self).add::<dyn #TraitPath>(|t| t, |t| t);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use heck::ToSnakeCase;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
||||
use crate::hygiene::Hygiene;
|
||||
|
||||
// Source:
|
||||
//
|
||||
// ```
|
||||
// #[salsa::db_view]
|
||||
// pub trait $Db: ... {
|
||||
// ...
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// becomes
|
||||
//
|
||||
// ```
|
||||
// pub trait $Db: __SalsaViewAs$Db__ {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// pub trait __SalsaViewAs$Db__ {
|
||||
// fn __salsa_add_view_for_$db__(&self);
|
||||
// }
|
||||
//
|
||||
// impl<T: Db> __SalsaViewAs$Db__ for T {
|
||||
// ...
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn db_view(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
syn::parse_macro_input!(args as syn::parse::Nothing);
|
||||
let db_view_macro = DbViewMacro::new(
|
||||
Hygiene::from(&input),
|
||||
syn::parse_macro_input!(input as syn::ItemTrait),
|
||||
);
|
||||
|
||||
match db_view_macro.expand() {
|
||||
Ok(tokens) => tokens.into(),
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) struct DbViewMacro {
|
||||
hygiene: Hygiene,
|
||||
input: syn::ItemTrait,
|
||||
DbViewTrait: syn::Ident,
|
||||
db_view_method: syn::Ident,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl DbViewMacro {
|
||||
// This is a case where our hygiene mechanism is inadequate.
|
||||
//
|
||||
// We cannot know whether `DbViewTrait` is defined elsewhere
|
||||
// in the module.
|
||||
//
|
||||
// Therefore we give it a dorky name.
|
||||
|
||||
pub(crate) fn db_view_trait_name(input: &impl Display) -> syn::Ident {
|
||||
syn::Ident::new(&format!("__SalsaAddView{}__", input), Span::call_site())
|
||||
}
|
||||
|
||||
pub(crate) fn db_view_method_name(input: &impl Display) -> syn::Ident {
|
||||
syn::Ident::new(
|
||||
&format!("__salsa_add_view_{}__", input.to_string().to_snake_case()),
|
||||
Span::call_site(),
|
||||
)
|
||||
}
|
||||
|
||||
fn new(hygiene: Hygiene, input: syn::ItemTrait) -> Self {
|
||||
Self {
|
||||
DbViewTrait: Self::db_view_trait_name(&input.ident),
|
||||
db_view_method: Self::db_view_method_name(&input.ident),
|
||||
hygiene,
|
||||
input,
|
||||
}
|
||||
}
|
||||
|
||||
fn expand(mut self) -> syn::Result<TokenStream> {
|
||||
self.add_supertrait();
|
||||
let view_impl = self.view_impl();
|
||||
let view_trait = self.view_trait();
|
||||
|
||||
let input = self.input;
|
||||
Ok(quote! {
|
||||
#input
|
||||
#view_trait
|
||||
#view_impl
|
||||
})
|
||||
}
|
||||
|
||||
fn add_supertrait(&mut self) {
|
||||
let Self { DbViewTrait, .. } = self;
|
||||
self.input.supertraits.push(parse_quote! { #DbViewTrait })
|
||||
}
|
||||
|
||||
fn view_trait(&self) -> syn::ItemTrait {
|
||||
let Self {
|
||||
DbViewTrait,
|
||||
db_view_method,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let vis = &self.input.vis;
|
||||
parse_quote! {
|
||||
/// Internal salsa method generated by the `salsa::db_view` macro
|
||||
/// that registers this database view trait with the salsa database.
|
||||
///
|
||||
/// Nothing to see here.
|
||||
#[doc(hidden)]
|
||||
#vis trait #DbViewTrait {
|
||||
fn #db_view_method(&self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view_impl(&self) -> syn::Item {
|
||||
let Self {
|
||||
DbViewTrait,
|
||||
db_view_method,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let DB = self.hygiene.ident("DB");
|
||||
let Database = self.hygiene.ident("Database");
|
||||
let views = self.hygiene.ident("views");
|
||||
let UserTrait = &self.input.ident;
|
||||
|
||||
parse_quote! {
|
||||
const _: () = {
|
||||
use salsa::Database as #Database;
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<#DB: #Database> #DbViewTrait for #DB {
|
||||
/// Internal salsa method generated by the `salsa::db_view` macro
|
||||
/// that registers this database view trait with the salsa database.
|
||||
///
|
||||
/// Nothing to see here.
|
||||
fn #db_view_method(&self) {
|
||||
let #views = self.views_of_self();
|
||||
#views.add::<dyn #UserTrait>(|t| t, |t| t);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,7 +42,6 @@ mod accumulator;
|
|||
mod configuration;
|
||||
mod db;
|
||||
mod db_lifetime;
|
||||
mod db_view;
|
||||
mod debug;
|
||||
mod debug_with_db;
|
||||
mod input;
|
||||
|
|
|
@ -41,7 +41,6 @@ pub use self::runtime::Runtime;
|
|||
pub use self::storage::Storage;
|
||||
pub use salsa_macros::accumulator;
|
||||
pub use salsa_macros::db;
|
||||
pub use salsa_macros::db_view;
|
||||
pub use salsa_macros::input;
|
||||
pub use salsa_macros::interned;
|
||||
pub use salsa_macros::jar;
|
||||
|
|
|
@ -14,6 +14,10 @@ use crate::Database;
|
|||
|
||||
use super::ParallelDatabase;
|
||||
|
||||
pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
|
||||
DatabaseGen::views(db)
|
||||
}
|
||||
|
||||
/// Salsa database methods whose implementation is generated by
|
||||
/// the `#[salsa::database]` procedural macro.
|
||||
///
|
||||
|
@ -43,11 +47,6 @@ pub unsafe trait DatabaseGen: Any + Send + Sync {
|
|||
/// Returns a reference to the underlying.
|
||||
fn views(&self) -> &Views;
|
||||
|
||||
/// Returns the upcasts database, tied to the type of `Self`; cannot be used from `dyn DatabaseGen` objects.
|
||||
fn views_of_self(&self) -> &ViewsOf<Self>
|
||||
where
|
||||
Self: Sized + Database;
|
||||
|
||||
/// Returns the nonce for the underyling storage.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
Loading…
Reference in a new issue