[−][src]Macro v9::decl_table
Defines a table. This is the most important item in the crate!
Usage
This macro is called decl_table!
, but it's nicer to use it as #[v9::table]
. This spares you
some indentation, and lets IDEs do "jump to definition".
// Declare a couple tables. // Note the snake_casing. The table macro actually epxands this into a module. #[v9::table] pub struct cheeses { pub quantity: f64, // NOTE: You should generally use absolute paths. You may get weird errors otherwise. :( pub warehouse: crate::warehouses::Id, pub stinky: bool, } #[v9::table] pub struct warehouses { pub coordinates: (i32, i32), pub on_fire: bool, } fn main() { // We create a new Universe. The Universe holds everything! use v9::prelude::Universe; let mut universe = Universe::new(); // It doesn't know about the tables until we register them. use v9::prelude::Register; cheeses::Marker::register(&mut universe); warehouses::Marker::register(&mut universe); // Let's write a closure to print out the inventory. let print_inventory = |cheeses: cheeses::Read, warehouses: warehouses::Read| { println!(" Warehouses:"); for id in warehouses.iter() { println!(" {:?}", warehouses.ref_row(id)); } println!(" Cheeses:"); for id in cheeses.iter() { println!(" {:?}", cheeses.ref_row(id)); } }; // The kernel holds our closure, and keeps track of all of the arguments it requires. use v9::kernel::Kernel; let mut print_inventory = Kernel::new(print_inventory); println!("An empty inventory:"); universe.run(&mut print_inventory); // If we don't care allocation, you can use kmap. It reduces noise. // Let's use it add some things: universe.kmap(|mut warehouses: warehouses::Write, mut cheeses: cheeses::Write| { let w0 = warehouses.push(warehouses::Row { coordinates: (1, 2), on_fire: true, }); let w1 = warehouses.push(warehouses::Row { coordinates: (2, 4), on_fire: false, }); let w2 = warehouses.push(warehouses::Row { coordinates: (4, 2), on_fire: true, }); cheeses.reserve(30); for wid in &[w0, w1, w2] { for _ in 0..10 { cheeses.push(cheeses::Row { quantity: 237.0, warehouse: *wid, stinky: true, }); } } }); println!("A non-empty inventory:"); universe.run(&mut print_inventory); // But what about those warehouses that are on fire? universe.kmap(|list: &mut warehouses::Ids, mut on_fire: warehouses::edit::on_fire| { let mut have_extinguisher = true; for wid in list.removing() { if on_fire[wid] { if have_extinguisher { have_extinguisher = false; on_fire[wid] = false; } else { wid.remove(); } } } }); // v9 has ensured data consistency. // The burnt cheese has been destroyed. println!("A diminished inventory:"); universe.run(&mut print_inventory); universe.kmap(|cheeses: cheeses::Read| { assert_eq!( 20, cheeses.iter().count(), ); }); }
Output
Details
There's several things to be aware of.
- Naming. The item name should be snake case (it becomes a module), and plural. The names of
columns should be singular, because they will be used like
students.mailing_address[student_id]
. (Unless the element itself is plural, eg ifstudents.known_aliases[student_id]
is aVec<String>
.) - The macro syntax kind of looks like a struct… but it very much is not.
- Type paths should be absolute, not relative.
- The "struct"'s visiblity may be anything, but the fields are always
pub
. - Each column must have a unique element type. A table with columns
age: u64, income: u64
will not work. You can wrap the structs in a newtype. (I have created the cratenew_units
to help cope with this.) Or if you don't care about memory access patterns you can combine the columns into a single Array Of Structs column.
Meta-Attributes
There are certain meta-attributes that may be placed on the "struct". Due to macro_rules
silliness, they must be given in the order listed here:
- Documentation. It is placed on the generated module.
#[row::<meta>]
* Passes meta-attributes to the generatedstruct Row
; eg#[row::derive(serde::Serialize))]
.#[row::derive(Clone, Debug)]
is always provided. (If your type is inconvenient to clone, consider wrapping it in anArc
, or something that panics.)#[raw_index(u32)]
. Defines the type used to index. The default isu32
. Must beRaw
. The last index is generally considered to be 'invalid'.
Any attributes on the columns will be passed as-is to the fields on Row
.
Example
v9::decl_table! { /// Some of our many fine cheeses! #[row::derive(serde::Serialize)] #[row::doc = "Why does our cheese keep catching on fire!??"] #[raw_index(u8)] pub struct cheeses { pub quantity: u64, /// P. U.! pub stinky: bool, #[serde(skip)] pub on_fire: Option<bool>, } }