1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use super::utils::is_copy_type;
use crate::ast;
use proc_macro2::{Span, TokenStream};
use quote::quote;

pub(super) fn generate_loader_functions(
    schema: &ast::DatabaseSchema,
) -> (TokenStream, TokenStream) {
    let mut function_tokens = TokenStream::new();
    let mut cache_field_tokens = TokenStream::new();
    for relation in schema.relations.iter().chain(&schema.derived_relations) {
        let ast::Relation {
            ref name,
            ref parameters,
            ..
        } = relation;
        let relation_hash = relation.get_hash();
        let file_name = format!("relations/{}", name);
        let load_fn_name = syn::Ident::new(&format!("load_{}", name), Span::call_site());
        let store_fn_name = syn::Ident::new(&format!("store_{}", name), Span::call_site());
        let mut types = TokenStream::new();
        for ast::RelationParameter { typ, .. } in parameters {
            types.extend(quote! {#typ,});
        }
        cache_field_tokens.extend(quote! {
            #name: std::cell::RefCell<Option<Vec<(#types)>>>,
        });
        function_tokens.extend(quote! {
            pub fn #load_fn_name(&self) -> std::cell::Ref<Vec<(#types)>> {
                if self.#name.borrow().is_none() {
                    let relation: Relation<(#types)> = unsafe { Relation::load(
                        #relation_hash,
                        self.database_root.join(#file_name)
                    ) }.unwrap();
                    *self.#name.borrow_mut() = Some(relation.into());
                }
                std::cell::Ref::map(self.#name.borrow(), |option| option.as_ref().unwrap())
            }
            pub fn #store_fn_name(&self, facts: Vec<(#types)>) {
                assert!(self.#name.borrow().is_none());
                let relation: Relation<(#types)> = facts.into();
                unsafe { relation.save(#relation_hash, self.database_root.join(#file_name)); }
                *self.#name.borrow_mut() = Some(relation.into());
            }
        });
    }
    for table in &schema.interning_tables {
        let ast::InterningTable { name, key, value } = table;
        let fn_name = syn::Ident::new(&format!("load_{}", name), Span::call_site());
        let fn_name_as_vec = syn::Ident::new(&format!("load_{}_as_vec", name), Span::call_site());
        let key_type = &key.name;
        let mut types = TokenStream::new();
        types.extend(quote! {#key_type,});
        match value {
            syn::Type::Tuple(syn::TypeTuple { elems, .. }) => {
                for elem in elems {
                    types.extend(quote! {#elem,});
                }
            }
            _ => {
                types.extend(quote! {#value,});
            }
        }
        let load = if is_copy_type(value, schema) {
            let table_hash = table.get_hash();
            let file_name = format!("interning/{}", name);
            quote! {
                unsafe {
                    InterningTable::load(
                        #table_hash,
                        self.database_root.join(#file_name)
                    ).unwrap()
                }
            }
        } else {
            let file_name = format!("interning/{}.bincode", name);
            quote! {
                crate::storage::load(
                    &self.database_root.join(#file_name)
                ).unwrap()
            }
        };
        cache_field_tokens.extend(quote! {
            #name: std::cell::RefCell<Option<InterningTable<#key_type, #value>>>,
        });
        function_tokens.extend(quote! {
            pub fn #fn_name(&self) -> std::cell::Ref<InterningTable<#key_type, #value>> {
                if self.#name.borrow().is_none() {
                    *self.#name.borrow_mut() = Some(#load);
                }
                std::cell::Ref::map(self.#name.borrow(), |option| option.as_ref().unwrap())
            }
            pub fn #fn_name_as_vec(&self) -> Vec<(#types)> {
                let table: InterningTable<#key_type, #value> = #load;
                table.into()
            }
        });
    }
    (function_tokens, cache_field_tokens)
}