Last active
August 29, 2015 14:02
-
-
Save ben0x539/ebfe2be7068eedd33072 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(macro_rules)] | |
use std::task; | |
use std::any::{Any, AnyRefExt}; | |
use std::io; | |
use std::iter; | |
// a channel for transmitting failure context info while unwinding | |
local_data_key!(diag_tx_key: Sender<FailureContext>) | |
// trait for extra failure information for the caller, | |
// kinda like Any but printable | |
trait Diag: Any { | |
fn with_msg(&self, |&str|); | |
} | |
// copy of Any's impl | |
impl<'a> AnyRefExt<'a> for &'a Diag { | |
#[inline] | |
fn is<T: 'static>(self) -> bool { | |
use std::intrinsics::TypeId; | |
// Get TypeId of the type this function is instantiated with | |
let t = TypeId::of::<T>(); | |
// Get TypeId of the type in the trait object | |
let boxed = self.get_type_id(); | |
// Compare both TypeIds on equality | |
t == boxed | |
} | |
#[inline] | |
fn as_ref<T: 'static>(self) -> Option<&'a T> { | |
use std::mem::{transmute, transmute_copy}; | |
use std::raw::TraitObject; | |
if self.is::<T>() { | |
unsafe { | |
// Get the raw representation of the trait object | |
let to: TraitObject = transmute_copy(&self); | |
// Extract the data pointer | |
Some(transmute(to.data)) | |
} | |
} else { | |
None | |
} | |
} | |
} | |
// basic impls for failure information, | |
// user-defined implementations would probably be for structs with | |
// extra fields to extract more details about the failure situation | |
impl Diag for String { | |
fn with_msg(&self, f: |&str|) { | |
f(self.as_slice()); | |
} | |
} | |
impl Diag for &'static str { | |
fn with_msg(&self, f: |&str|) { | |
f(*self); | |
} | |
} | |
// wrap the above with code location info | |
struct FailureContext { | |
file: &'static str, | |
line: uint, | |
diag: Box<Diag:Send>, | |
} | |
// RAII-invoked dispatcher for FailureContext values | |
struct Diagnoser<'a> { | |
diag: ||:'a -> Box<Diag:Send>, | |
file: &'static str, | |
line: uint, | |
} | |
#[unsafe_destructor] | |
impl<'a> Drop for Diagnoser<'a> { | |
fn drop(&mut self) { | |
if !task::failing() { return; } | |
for tx in diag_tx_key.get().iter() { | |
drop(tx.send_opt(FailureContext { | |
file: self.file, | |
line: self.line, | |
diag: (self.diag)(), | |
})); | |
} | |
} | |
} | |
// slightly more palatable syntax for wrapping code in a failure context: | |
// with_diag! { "doing a certain thing": { | |
// ... | |
// }} | |
macro_rules! with_diag { | |
($d:expr: $code:block) => ({ | |
let _diagnoser = Diagnoser { | |
diag: || { box () ($d) as Box<Diag:Send> }, | |
file: file!(), | |
line: line!() | |
}; | |
$code | |
}) | |
} | |
// package up the regular Any task failure reason with the received | |
// context info, in order | |
struct DiagFailure { | |
cause: Box<Any:Send>, | |
diagnostics: Vec<FailureContext>, | |
} | |
// encapsulate failure in a task as usual, but read out extra diagnostics | |
fn task_try_diag<T: Send>(f: proc():Send -> T) -> Result<T, DiagFailure> { | |
let (tx, rx) = std::comm::channel(); | |
let result = task::try(proc() { | |
diag_tx_key.replace(Some(tx)); | |
f() | |
}); | |
result.map_err(|cause| { | |
DiagFailure { | |
cause: cause, | |
diagnostics: rx.iter().collect(), | |
} | |
}) | |
} | |
// some basic, generic output | |
fn report_failure(f: &DiagFailure) { | |
let &DiagFailure { ref cause, ref diagnostics } = f; | |
io::print("failure in child task: "); | |
let cause_msg: Option<&&'static str> = cause.as_ref(); | |
for &s in cause_msg.iter() { io::print(*s); } | |
let cause_msg: Option<&String> = cause.as_ref(); | |
for &s in cause_msg.iter() { io::print(s.as_slice()); } | |
io::print("\n"); | |
for context in diagnostics.iter() { | |
let (file, line) = (context.file, context.line); | |
context.diag.with_msg(|s| println!(" {}:{}: {}", file, line, s)); | |
} | |
} | |
struct TypeFilter<T, I> { | |
inner: I | |
} | |
fn type_filter<'a, T: 'static, I: iter::Iterator<&'a Box<Diag:Send>>>(i: I) -> TypeFilter<T, I> { | |
TypeFilter { inner: i } | |
} | |
impl<'a, T: 'static, I: iter::Iterator<&'a Box<Diag:Send>>> Iterator<&'a T> for TypeFilter<T, I> { | |
fn next(&mut self) -> Option<&'a T> { | |
loop { | |
let any = match self.inner.next() { | |
None => break, | |
Some(any) => any, | |
}; | |
let opt_t: Option<&'a T> = any.as_ref(); | |
let r = match opt_t { | |
None => continue, | |
Some(r) => r, | |
}; | |
return Some(r); | |
} | |
None | |
} | |
} | |
// --- | |
// sample failiing code | |
fn f() { | |
io::println("doing some tasky things..."); | |
with_diag! { "inside super important subcomponent code": { | |
g(); | |
}} | |
} | |
fn processing_file(filename: &str) -> WhileProcessingFile { | |
WhileProcessingFile { filename: filename.to_string() } | |
} | |
struct WhileProcessingFile { | |
filename: String | |
} | |
impl Diag for WhileProcessingFile { | |
fn with_msg(&self, f: |&str|) { | |
let msg = format!("while processing file `{}`", self.filename); | |
f(msg.as_slice()); | |
} | |
} | |
fn g() { | |
io::println("still tasking..."); | |
let filename = "test.png"; | |
with_diag! { processing_file(filename): { | |
let mut file = io::File::open(&Path::new(filename)).unwrap(); | |
let contents = file.read_to_end().unwrap(); | |
// produce unhelpful "unwrapping None" failure message | |
let clearly_its_all_text = | |
std::str::from_utf8(contents.as_slice()).unwrap(); | |
io::println(clearly_its_all_text); | |
// ... | |
}} | |
} | |
fn main() { | |
let result = task_try_diag(proc() { | |
f(); | |
io::println("hooray, success?"); | |
}); | |
io::println("back in main task"); | |
for d in result.err().iter() { | |
report_failure(d); | |
for context in type_filter(d.diagnostics.iter().map(|ctxt| &ctxt.diag)) { | |
let context: &WhileProcessingFile = context; | |
println!("clearly the failure is related to file `{}`!", | |
context.filename); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
doing some tasky things... | |
still tasking... | |
task '<unnamed>' failed at 'called `Option::unwrap()` on a `None` value', [...]/rust/src/libcore/option.rs:265 | |
back in main task | |
failure in child task: called `Option::unwrap()` on a `None` value | |
diag.rs:202: while processing file `test.png` | |
diag.rs:181: inside super important subcomponent code | |
clearly the failure is related to file `test.png`! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment