Skip to content

Commit

Permalink
Add unwrap macro.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirbaio committed Nov 24, 2020
1 parent 6467edf commit 23b2cfd
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 14 deletions.
4 changes: 4 additions & 0 deletions firmware/qemu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ test = false
name = "double-write"
test = false

[[bin]]
name = "unwrap"
test = false

[[bin]]
name = "alloc"
test = false
Expand Down
3 changes: 3 additions & 0 deletions firmware/qemu/src/bin/unwrap.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0.000000 INFO The answer is 42
0.000000 ERROR panicked at 'unwrap failed: x'
error: `Bar`
3 changes: 3 additions & 0 deletions firmware/qemu/src/bin/unwrap.release.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0.000000 INFO The answer is 42
0.000000 ERROR panicked at 'unwrap failed: x'
error: `Bar`
33 changes: 33 additions & 0 deletions firmware/qemu/src/bin/unwrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use cortex_m_semihosting::debug;

use defmt_semihosting as _; // global logger

#[derive(defmt::Format)]
enum Error {
Bar,
}

#[entry]
fn main() -> ! {
let x: Result<u32, Error> = Ok(42);
defmt::info!("The answer is {:?}", defmt::unwrap!(x));
let x: Result<u32, Error> = Err(Error::Bar);
defmt::info!("The answer is {:?}", defmt::unwrap!(x));

loop {
debug::exit(debug::EXIT_SUCCESS)
}
}

// like `panic-semihosting` but doesn't print to stdout (that would corrupt the defmt stream)
#[cfg(target_os = "none")]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {
debug::exit(debug::EXIT_SUCCESS)
}
}
1 change: 1 addition & 0 deletions firmware/qemu/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ test "assert"
test "assert-eq"
test "assert-ne"
test "double-write"
test "unwrap"
if rustc -V | grep nightly; then
test "alloc" "alloc"
fi
80 changes: 66 additions & 14 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,20 +653,7 @@ fn assert_binop(ts: TokenStream, binop: BinOp) -> TokenStream {
};

for val in vals {
let mut segments = Punctuated::new();
segments.push(PathSegment {
ident: Ident2::new(*val, Span2::call_site()),
arguments: PathArguments::None,
});

log_args.push(Expr::Path(ExprPath {
attrs: vec![],
qself: None,
path: Path {
leading_colon: None,
segments,
},
}));
log_args.push(ident_expr(*val));
}

let log_stmt = match binop {
Expand Down Expand Up @@ -752,6 +739,71 @@ pub fn debug_assert_ne_(ts: TokenStream) -> TokenStream {
.into()
}

#[proc_macro]
pub fn unwrap(ts: TokenStream) -> TokenStream {
let assert = parse_macro_input!(ts as Assert);

let condition = assert.condition;
let log_stmt = if let Some(args) = assert.args {
log(
Level::Error,
FormatArgs {
litstr: LitStr::new(
&format!("panicked at '{}'", args.litstr.value()),
Span2::call_site(),
),
rest: args.rest,
},
)
} else {
let mut log_args = Punctuated::new();
log_args.push(ident_expr("_unwrap_err"));

log(
Level::Error,
FormatArgs {
litstr: LitStr::new(
&format!(
"panicked at 'unwrap failed: {}'
error: `{{:?}}`",
escape_expr(&condition)
),
Span2::call_site(),
),
rest: Some((syn::token::Comma::default(), log_args)),
},
)
};

quote!(
match defmt::export::into_result(#condition) {
::core::result::Result::Ok(res) => res,
::core::result::Result::Err(_unwrap_err) => {
#log_stmt;
defmt::export::panic()
}
}
)
.into()
}

fn ident_expr(name: &str) -> Expr {
let mut segments = Punctuated::new();
segments.push(PathSegment {
ident: Ident2::new(name, Span2::call_site()),
arguments: PathArguments::None,
});

Expr::Path(ExprPath {
attrs: vec![],
qself: None,
path: Path {
leading_colon: None,
segments,
},
})
}

fn escape_expr(expr: &Expr) -> String {
let q = quote!(#expr);
q.to_string().replace("{", "{{").replace("}", "}}")
Expand Down
46 changes: 46 additions & 0 deletions src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ pub fn istr(address: usize) -> Str {
}

mod sealed {
use crate as defmt;
use crate::{Format, Formatter};
use defmt_macros::internp;

pub trait Truncate<U> {
fn truncate(self) -> U;
}
Expand Down Expand Up @@ -137,12 +141,54 @@ mod sealed {
self
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;

impl Format for NoneError {
fn format(&self, fmt: &mut Formatter) {
if fmt.needs_tag() {
let t = internp!("Unwrap of a None option value");
fmt.u8(&t);
}
}
}

pub trait IntoResult {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}

impl<T> IntoResult for Option<T> {
type Ok = T;
type Error = NoneError;

#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}

impl<T, E> IntoResult for Result<T, E> {
type Ok = T;
type Error = E;

#[inline]
fn into_result(self) -> Self {
self
}
}
}

pub fn truncate<T>(x: impl sealed::Truncate<T>) -> T {
x.truncate()
}

pub fn into_result<T: sealed::IntoResult>(x: T) -> Result<T::Ok, T::Error> {
x.into_result()
}

/// For testing purposes
#[cfg(target_arch = "x86_64")]
pub fn panic() -> ! {
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ pub use defmt_macros::todo_ as unimplemented;
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::panic_ as panic;

/// todo
///
/// If used, the format string must follow the defmt syntax (documented in [the manual])
///
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
pub use defmt_macros::unwrap;

/// Overrides the panicking behavior of `defmt::panic!`
///
/// By default, `defmt::panic!` calls `core::panic!` after logging the panic message using `defmt`.
Expand Down

0 comments on commit 23b2cfd

Please sign in to comment.