mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-08 21:35:47 +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 proc_macro2::TokenStream;
|
||||||
use syn::{parse::Nothing, ItemStruct};
|
use syn::parse::Nothing;
|
||||||
|
|
||||||
use crate::hygiene::Hygiene;
|
use crate::hygiene::Hygiene;
|
||||||
|
|
||||||
// Source:
|
// Source:
|
||||||
//
|
//
|
||||||
// #[salsa::db(Jar0, Jar1, Jar2)]
|
// #[salsa::db]
|
||||||
// pub struct Database {
|
// pub struct Database {
|
||||||
// storage: salsa::Storage<Self>,
|
// storage: salsa::Storage<Self>,
|
||||||
// }
|
// }
|
||||||
|
@ -15,11 +15,10 @@ pub(crate) fn db(
|
||||||
input: proc_macro::TokenStream,
|
input: proc_macro::TokenStream,
|
||||||
) -> proc_macro::TokenStream {
|
) -> proc_macro::TokenStream {
|
||||||
let _nothing = syn::parse_macro_input!(args as Nothing);
|
let _nothing = syn::parse_macro_input!(args as Nothing);
|
||||||
let db_macro = DbMacro {
|
let hygiene = Hygiene::from(&input);
|
||||||
hygiene: Hygiene::from(&input),
|
let input = syn::parse_macro_input!(input as syn::Item);
|
||||||
input: syn::parse_macro_input!(input as syn::ItemStruct),
|
let db_macro = DbMacro { hygiene };
|
||||||
};
|
match db_macro.try_db(input) {
|
||||||
match db_macro.try_db() {
|
|
||||||
Ok(v) => v.into(),
|
Ok(v) => v.into(),
|
||||||
Err(e) => e.to_compile_error().into(),
|
Err(e) => e.to_compile_error().into(),
|
||||||
}
|
}
|
||||||
|
@ -27,22 +26,43 @@ pub(crate) fn db(
|
||||||
|
|
||||||
struct DbMacro {
|
struct DbMacro {
|
||||||
hygiene: Hygiene,
|
hygiene: Hygiene,
|
||||||
input: ItemStruct,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
impl DbMacro {
|
impl DbMacro {
|
||||||
fn try_db(self) -> syn::Result<TokenStream> {
|
fn try_db(self, input: syn::Item) -> syn::Result<TokenStream> {
|
||||||
let has_storage_impl = self.has_storage_impl()?;
|
match input {
|
||||||
let input = self.input;
|
syn::Item::Struct(input) => {
|
||||||
Ok(quote! {
|
let has_storage_impl = self.has_storage_impl(&input)?;
|
||||||
#has_storage_impl
|
Ok(quote! {
|
||||||
#input
|
#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";
|
let storage = "storage";
|
||||||
for field in self.input.fields.iter() {
|
for field in input.fields.iter() {
|
||||||
if let Some(i) = &field.ident {
|
if let Some(i) = &field.ident {
|
||||||
if i == storage {
|
if i == storage {
|
||||||
return Ok(i.clone());
|
return Ok(i.clone());
|
||||||
|
@ -56,15 +76,14 @@ impl DbMacro {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
&self.input.ident,
|
&input.ident,
|
||||||
"database struct must be a braced struct (`{}`) with a field named `storage`",
|
"database struct must be a braced struct (`{}`) with a field named `storage`",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
fn has_storage_impl(&self, input: &syn::ItemStruct) -> syn::Result<TokenStream> {
|
||||||
fn has_storage_impl(&self) -> syn::Result<TokenStream> {
|
let storage = self.find_storage_field(input)?;
|
||||||
let storage = self.find_storage_field()?;
|
let db = &input.ident;
|
||||||
let db = &self.input.ident;
|
|
||||||
|
|
||||||
let SalsaHasStorage = self.hygiene.ident("SalsaHasStorage");
|
let SalsaHasStorage = self.hygiene.ident("SalsaHasStorage");
|
||||||
let SalsaStorage = self.hygiene.ident("SalsaStorage");
|
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 configuration;
|
||||||
mod db;
|
mod db;
|
||||||
mod db_lifetime;
|
mod db_lifetime;
|
||||||
mod db_view;
|
|
||||||
mod debug;
|
mod debug;
|
||||||
mod debug_with_db;
|
mod debug_with_db;
|
||||||
mod input;
|
mod input;
|
||||||
|
|
|
@ -41,7 +41,6 @@ pub use self::runtime::Runtime;
|
||||||
pub use self::storage::Storage;
|
pub use self::storage::Storage;
|
||||||
pub use salsa_macros::accumulator;
|
pub use salsa_macros::accumulator;
|
||||||
pub use salsa_macros::db;
|
pub use salsa_macros::db;
|
||||||
pub use salsa_macros::db_view;
|
|
||||||
pub use salsa_macros::input;
|
pub use salsa_macros::input;
|
||||||
pub use salsa_macros::interned;
|
pub use salsa_macros::interned;
|
||||||
pub use salsa_macros::jar;
|
pub use salsa_macros::jar;
|
||||||
|
|
|
@ -14,6 +14,10 @@ use crate::Database;
|
||||||
|
|
||||||
use super::ParallelDatabase;
|
use super::ParallelDatabase;
|
||||||
|
|
||||||
|
pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
|
||||||
|
DatabaseGen::views(db)
|
||||||
|
}
|
||||||
|
|
||||||
/// Salsa database methods whose implementation is generated by
|
/// Salsa database methods whose implementation is generated by
|
||||||
/// the `#[salsa::database]` procedural macro.
|
/// the `#[salsa::database]` procedural macro.
|
||||||
///
|
///
|
||||||
|
@ -43,11 +47,6 @@ pub unsafe trait DatabaseGen: Any + Send + Sync {
|
||||||
/// Returns a reference to the underlying.
|
/// Returns a reference to the underlying.
|
||||||
fn views(&self) -> &Views;
|
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.
|
/// Returns the nonce for the underyling storage.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
Loading…
Reference in a new issue