macro_rules! decl_table {
(
$(#[doc = $doc:literal])*
$(#[row::$row_meta:meta])*
$vis:vis struct $name:ident {
$(
$(#[$cmeta:meta])*
pub $cn:ident: $cty:ty,
)*
}
) => { ... };
(
$(#[doc = $doc:literal])*
$(#[row::$row_meta:meta])*
#[raw_index($raw:ty)]
$vis:vis struct $name:ident {
$(
$(#[$cmeta:meta])*
pub $cn:ident: $cty:ty,
)*
}
// FIXME: `in mod $in_mod:tt`
) => { ... };
}Expand description
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: u64will not work. You can wrap the structs in a newtype. (I have created the cratenew_unitsto 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>,
}
}