Skip to content

Instantly share code, notes, and snippets.

@mitsuhiko
Created February 19, 2013 17:59
Show Gist options
  • Save mitsuhiko/213892b73873f9d20920 to your computer and use it in GitHub Desktop.
Save mitsuhiko/213892b73873f9d20920 to your computer and use it in GitHub Desktop.
/* a macro that defines a context variable that is stored in thread local
storage. It's implemented as a module that exports a `get`, `set` and
`set_to` function. `set_to` acts as a stack. */
macro_rules! _context_var_impl (($name:ident : $t:ty, $primer:expr, $always_set:expr) => (
mod $name {
/* internal tls key helper */
fn key(_x: @$t) {}
/* primes a context variable from a callback. This is implemented as a
helper function to produce nicer error messages on mismatch */
fn prime(f: fn() -> @$t) -> @$t {
let x = f();
set(x);
x
}
/* checks if the variable is set. Primed context vars are always set. */
pub fn is_set() -> bool { unsafe {
match task::local_data::local_data_get(key) {
Some(ref _x) => true,
None => $always_set
}
} }
/* returns the current value as option */
pub fn get_option() -> Option<@$t> { unsafe {
match task::local_data::local_data_get(key) {
None => match $primer {
Some(f) => Some(prime(f)),
None => None
},
Some(x) => Some(x)
}
} }
/* returns the current value of that variable or fails with an error */
pub fn get() -> @$t {
match get_option() {
Some(x) => x,
None => fail!(fmt!("%s::get() tried to return value \
but no value was set",
stringify!($name)))
}
}
/* sets the variable to a new value */
pub fn set(value: @$t) { unsafe {
task::local_data::local_data_set(key, value);
} }
/* unsets the value. If the context variable is primed it will be
primed again next call. */
pub fn unset() { unsafe {
task::local_data::local_data_pop(key);
} }
/* temporarily overrides the variable with a new value */
pub fn set_to(value: @$t, cb: fn()) { unsafe {
let old = task::local_data::local_data_pop(key);
task::local_data::local_data_set(key, value);
cb();
match (old) {
None => {},
Some(x) => task::local_data::local_data_set(key, x)
}
} }
}
))
/* creates a regular context var */
macro_rules! context_var (($name:ident : $t:ty) => (
_context_var_impl!($name : $t, None, false)
))
/* creates a context var that is primed from a stack closure */
macro_rules! context_var_primed(($name:ident : $t:ty, $primer:expr) => (
_context_var_impl!($name : $t, Some($primer), true)
))
#[cfg(test)]
mod tests {
context_var!(language_simple: ~str)
context_var_primed!(language: ~str, || {
@~"en_US"
})
#[test]
fn test_simple() {
assert language_simple::get_option() == None;
do language_simple::set_to(@~"de_DE") {
assert language_simple::get_option() == Some(@~"de_DE");
}
assert language_simple::get_option() == None;
language_simple::set(@~"en_US");
assert language_simple::get_option() == Some(@~"en_US");
}
#[test]
fn test_primed() {
assert language::get_option() == Some(@~"en_US");
do language::set_to(@~"de_DE") {
assert language::get_option() == Some(@~"de_DE");
}
assert language::get_option() == Some(@~"en_US");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment