1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-05-09 10:28:46 +00:00

feat(core/rust/ui): return Layout results as singleton objects

[no changelog]
This commit is contained in:
Martin Milata 2022-02-19 13:49:04 +01:00
parent 4cf917c7cb
commit 38f4ab0983
26 changed files with 471 additions and 330 deletions

View File

@ -23,104 +23,6 @@
#include "librust.h" #include "librust.h"
/// def layout_new_confirm_action(
/// *,
/// title: str,
/// action: str | None = None,
/// description: str | None = None,
/// verb: str | None = None,
/// verb_cancel: str | None = None,
/// hold: bool | None = None,
/// reverse: bool = False,
/// ) -> object:
/// """Example layout."""
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_action_obj,
0, ui_layout_new_confirm_action);
#if TREZOR_MODEL == T
/// def layout_new_example(text: str) -> object:
/// """Example layout."""
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorui2_layout_new_example_obj,
ui_layout_new_example);
/// def layout_new_pin(
/// *,
/// prompt: str,
/// subprompt: str,
/// allow_cancel: bool,
/// warning: str | None,
/// ) -> object:
/// """PIN keyboard."""
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_pin_obj, 0,
ui_layout_new_pin);
/// def layout_new_passphrase(
/// *,
/// prompt: str,
/// max_len: int,
/// ) -> object:
/// """Passphrase keyboard."""
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_passphrase_obj, 0,
ui_layout_new_passphrase);
/// def layout_new_bip39(
/// *,
/// prompt: str,
/// ) -> object:
/// """BIP39 keyboard."""
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_bip39_obj, 0,
ui_layout_new_bip39);
/// def layout_new_slip39(
/// *,
/// prompt: str,
/// ) -> object:
/// """BIP39 keyboard."""
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_slip39_obj, 0,
ui_layout_new_slip39);
#elif TREZOR_MODEL == 1
/// def layout_new_confirm_text(
/// *,
/// title: str,
/// data: str,
/// description: str | None,
/// ) -> object:
/// """Example layout."""
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_text_obj, 0,
ui_layout_new_confirm_text);
#endif
STATIC const mp_rom_map_elem_t mp_module_trezorui2_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorui2)},
{MP_ROM_QSTR(MP_QSTR_layout_new_confirm_action),
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_action_obj)},
#if TREZOR_MODEL == T
{MP_ROM_QSTR(MP_QSTR_layout_new_example),
MP_ROM_PTR(&mod_trezorui2_layout_new_example_obj)},
{MP_ROM_QSTR(MP_QSTR_layout_new_pin),
MP_ROM_PTR(&mod_trezorui2_layout_new_pin_obj)},
{MP_ROM_QSTR(MP_QSTR_layout_new_passphrase),
MP_ROM_PTR(&mod_trezorui2_layout_new_passphrase_obj)},
{MP_ROM_QSTR(MP_QSTR_layout_new_bip39),
MP_ROM_PTR(&mod_trezorui2_layout_new_bip39_obj)},
{MP_ROM_QSTR(MP_QSTR_layout_new_slip39),
MP_ROM_PTR(&mod_trezorui2_layout_new_slip39_obj)},
#elif TREZOR_MODEL == 1
{MP_ROM_QSTR(MP_QSTR_layout_new_confirm_text),
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_text_obj)},
#endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorui2_globals,
mp_module_trezorui2_globals_table);
const mp_obj_module_t mp_module_trezorui2 = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&mp_module_trezorui2_globals,
};
MP_REGISTER_MODULE(MP_QSTR_trezorui2, mp_module_trezorui2, MP_REGISTER_MODULE(MP_QSTR_trezorui2, mp_module_trezorui2,
MICROPY_PY_TREZORUI2); MICROPY_PY_TREZORUI2);

View File

@ -60,6 +60,7 @@ fn generate_micropython_bindings() {
.allowlist_function("mp_obj_new_int_from_uint") .allowlist_function("mp_obj_new_int_from_uint")
.allowlist_function("mp_obj_new_bytes") .allowlist_function("mp_obj_new_bytes")
.allowlist_function("mp_obj_new_str") .allowlist_function("mp_obj_new_str")
.allowlist_function("mp_obj_new_tuple")
.allowlist_function("mp_obj_get_int_maybe") .allowlist_function("mp_obj_get_int_maybe")
.allowlist_function("mp_obj_is_true") .allowlist_function("mp_obj_is_true")
.allowlist_function("mp_call_function_n_kw") .allowlist_function("mp_call_function_n_kw")

View File

@ -12,19 +12,7 @@ mp_obj_t protobuf_debug_msg_type();
mp_obj_t protobuf_debug_msg_def_type(); mp_obj_t protobuf_debug_msg_def_type();
#endif #endif
mp_obj_t ui_layout_new_example(mp_obj_t); extern mp_obj_module_t mp_module_trezorui2;
mp_obj_t ui_layout_new_confirm_action(size_t n_args, const mp_obj_t *args,
mp_map_t *kwargs);
mp_obj_t ui_layout_new_confirm_text(size_t n_args, const mp_obj_t *args,
mp_map_t *kwargs);
mp_obj_t ui_layout_new_pin(size_t n_args, const mp_obj_t *args,
mp_map_t *kwargs);
mp_obj_t ui_layout_new_passphrase(size_t n_args, const mp_obj_t *args,
mp_map_t *kwargs);
mp_obj_t ui_layout_new_bip39(size_t n_args, const mp_obj_t *args,
mp_map_t *kwargs);
mp_obj_t ui_layout_new_slip39(size_t n_args, const mp_obj_t *args,
mp_map_t *kwargs);
#ifdef TREZOR_EMULATOR #ifdef TREZOR_EMULATOR
mp_obj_t ui_debug_layout_type(); mp_obj_t ui_debug_layout_type();

View File

@ -10,7 +10,18 @@ static void _librust_qstrs(void) {
MP_QSTR_MESSAGE_NAME; MP_QSTR_MESSAGE_NAME;
// layout // layout
MP_QSTR___name__;
MP_QSTR_trezorui2;
MP_QSTR_Layout; MP_QSTR_Layout;
MP_QSTR_CONFIRMED;
MP_QSTR_CANCELLED;
MP_QSTR_confirm_action;
MP_QSTR_confirm_text;
MP_QSTR_request_pin;
MP_QSTR_request_passphrase;
MP_QSTR_request_bip39;
MP_QSTR_request_slip39;
MP_QSTR_set_timer_fn; MP_QSTR_set_timer_fn;
MP_QSTR_touch_event; MP_QSTR_touch_event;
MP_QSTR_button_event; MP_QSTR_button_event;

View File

@ -175,6 +175,7 @@ macro_rules! obj_type {
macro_rules! obj_module { macro_rules! obj_module {
($($key:expr => $val:expr),*) => ({ ($($key:expr => $val:expr),*) => ({
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
#[allow(unused_doc_comments)]
unsafe { unsafe {
use $crate::micropython::ffi; use $crate::micropython::ffi;

View File

@ -289,6 +289,23 @@ impl TryFrom<&'static CStr> for Obj {
} }
} }
impl TryFrom<(Obj, Obj)> for Obj {
type Error = Error;
fn try_from(val: (Obj, Obj)) -> Result<Self, Self::Error> {
// SAFETY:
// - Should work with any micropython objects.
// EXCEPTION: Will raise if allocation fails.
let values = [val.0, val.1];
let obj = catch_exception(|| unsafe { ffi::mp_obj_new_tuple(2, values.as_ptr()) })?;
if obj.is_null() {
Err(Error::AllocationFailed)
} else {
Ok(obj)
}
}
}
// //
// # Additional conversions based on the methods above. // # Additional conversions based on the methods above.
// //

View File

@ -447,7 +447,7 @@ impl Grid {
} }
pub fn cells(&self, cells: GridCellSpan) -> Rect { pub fn cells(&self, cells: GridCellSpan) -> Rect {
let from = self.row_col(cells.from.0, cells.to.1); let from = self.row_col(cells.from.0, cells.from.1);
let to = self.row_col(cells.to.0, cells.to.1); let to = self.row_col(cells.to.0, cells.to.1);
from.union(to) from.union(to)
} }

View File

@ -1 +1,2 @@
pub mod obj; pub mod obj;
pub mod result;

View File

@ -14,7 +14,7 @@ use crate::{
}, },
time::Duration, time::Duration,
ui::{ ui::{
component::{Child, Component, Event, EventCtx, Never, PageMsg, TimerToken}, component::{Child, Component, Event, EventCtx, Never, TimerToken},
geometry::Rect, geometry::Rect,
}, },
util, util,
@ -38,13 +38,12 @@ pub trait ComponentMsgObj: Component {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error>; fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error>;
} }
impl<T> ComponentMsgObj for T impl<T> ComponentMsgObj for Child<T>
where where
T: Component, T: ComponentMsgObj,
T::Msg: TryInto<Obj, Error = Error>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
msg.try_into() self.inner().msg_try_into_obj(msg)
} }
} }
@ -333,17 +332,6 @@ impl TryFrom<Duration> for Obj {
} }
} }
impl<T> TryFrom<PageMsg<T, bool>> for Obj {
type Error = Error;
fn try_from(val: PageMsg<T, bool>) -> Result<Self, Self::Error> {
match val {
PageMsg::Content(_) => Err(Error::TypeError),
PageMsg::Controls(c) => Ok(c.into()),
}
}
}
impl From<Never> for Obj { impl From<Never> for Obj {
fn from(_: Never) -> Self { fn from(_: Never) -> Self {
unreachable!() unreachable!()

View File

@ -0,0 +1,34 @@
use crate::micropython::{
obj::{Obj, ObjBase},
qstr::Qstr,
typ::Type,
};
#[repr(C)]
pub struct ResultObj {
base: ObjBase,
}
impl ResultObj {
/// Convert ResultObj to a MicroPython object.
pub const fn as_obj(&'static self) -> Obj {
// SAFETY:
// - We are an object struct with a base and a type.
// - 'static lifetime holds us in place.
// - There's nothing to mutate.
unsafe { Obj::from_ptr(self as *const _ as *mut _) }
}
}
// SAFETY: We are in a single-threaded environment.
unsafe impl Sync for ResultObj {}
static CONFIRMED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CONFIRMED, };
static CANCELLED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CANCELLED, };
pub static CONFIRMED: ResultObj = ResultObj {
base: CONFIRMED_TYPE.as_base(),
};
pub static CANCELLED: ResultObj = ResultObj {
base: CANCELLED_TYPE.as_base(),
};

View File

@ -31,6 +31,10 @@ where
right_btn: right.map(Child::new), right_btn: right.map(Child::new),
} }
} }
pub fn inner(&self) -> &T {
self.content.inner()
}
} }
impl<T, U> Component for Dialog<T, U> impl<T, U> Component for Dialog<T, U>

View File

@ -23,6 +23,10 @@ where
content: Child::new(content), content: Child::new(content),
} }
} }
pub fn inner(&self) -> &T {
self.content.inner()
}
} }
impl<T, U> Component for Frame<T, U> impl<T, U> Component for Frame<T, U>

View File

@ -1,26 +1,52 @@
use core::convert::TryInto; use core::convert::TryInto;
use crate::{ use crate::{
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr}, error::Error,
micropython::{buffer::Buffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
ui::{ ui::{
component::{text::paragraphs::Paragraphs, FormattedText}, component::{
layout::obj::LayoutObj, base::Component,
model_t1::component::ButtonPos, paginated::{PageMsg, Paginate},
text::paragraphs::Paragraphs,
FormattedText,
},
layout::{
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED},
},
}, },
util, util,
}; };
use super::{ use super::{
component::{Button, ButtonPage, Dialog, DialogMsg, Frame}, component::{Button, ButtonPage, ButtonPos, Frame},
constant, theme, theme,
}; };
#[no_mangle] impl<T> ComponentMsgObj for ButtonPage<T>
extern "C" fn ui_layout_new_confirm_action( where
n_args: usize, T: Component + Paginate,
args: *const Obj, {
kwargs: *const Map, fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
) -> Obj { match msg {
PageMsg::Content(_) => Err(Error::TypeError),
PageMsg::Controls(true) => Ok(CONFIRMED.as_obj()),
PageMsg::Controls(false) => Ok(CANCELLED.as_obj()),
}
}
}
impl<T, U> ComponentMsgObj for Frame<T, U>
where
T: ComponentMsgObj,
U: AsRef<[u8]>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg)
}
}
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| { let block = |_args: &[Obj], kwargs: &Map| {
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
@ -39,9 +65,9 @@ extern "C" fn ui_layout_new_confirm_action(
_ => "", _ => "",
}; };
let left = verb_cancel let _left = verb_cancel
.map(|label| Button::with_text(ButtonPos::Left, label, theme::button_cancel())); .map(|label| Button::with_text(ButtonPos::Left, label, theme::button_cancel()));
let right = let _right =
verb.map(|label| Button::with_text(ButtonPos::Right, label, theme::button_default())); verb.map(|label| Button::with_text(ButtonPos::Right, label, theme::button_default()));
let obj = LayoutObj::new(Frame::new( let obj = LayoutObj::new(Frame::new(
@ -58,12 +84,7 @@ extern "C" fn ui_layout_new_confirm_action(
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
#[no_mangle] extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
extern "C" fn ui_layout_new_confirm_text(
n_args: usize,
args: *const Obj,
kwargs: *const Map,
) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| { let block = |_args: &[Obj], kwargs: &Map| {
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let data: Buffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?; let data: Buffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?;
@ -87,15 +108,49 @@ extern "C" fn ui_layout_new_confirm_text(
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
#[no_mangle]
pub static mp_module_trezorui2: Module = obj_module! {
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
/// CONFIRMED: object
Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(),
/// CANCELLED: object
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
/// def confirm_action(
/// *,
/// title: str,
/// action: str | None = None,
/// description: str | None = None,
/// verb: str | None = None,
/// verb_cancel: str | None = None,
/// hold: bool | None = None,
/// reverse: bool = False,
/// ) -> object:
/// """Confirm action."""
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
/// def confirm_text(
/// *,
/// title: str,
/// data: str,
/// description: str | None,
/// ) -> object:
/// """Confirm text."""
Qstr::MP_QSTR_confirm_text => obj_fn_kw!(0, new_confirm_text).as_obj(),
};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
error::Error,
trace::Trace, trace::Trace,
ui::{ ui::{
component::Component, component::Component,
display, model_t1::{
model_t1::component::{Dialog, DialogMsg}, component::{Dialog, DialogMsg},
constant,
},
}, },
}; };
@ -107,18 +162,16 @@ mod tests {
String::from_utf8(t).unwrap() String::from_utf8(t).unwrap()
} }
impl<T> TryFrom<DialogMsg<T>> for Obj impl<T, U> ComponentMsgObj for Dialog<T, U>
where where
Obj: TryFrom<T>, T: ComponentMsgObj,
Error: From<<T as TryInto<Obj>>::Error>, U: AsRef<[u8]>,
{ {
type Error = Error; fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
fn try_from(val: DialogMsg<T>) -> Result<Self, Self::Error> { DialogMsg::Content(c) => self.inner().msg_try_into_obj(c),
match val { DialogMsg::LeftClicked => Ok(CANCELLED.as_obj()),
DialogMsg::Content(c) => Ok(c.try_into()?), DialogMsg::RightClicked => Ok(CONFIRMED.as_obj()),
DialogMsg::LeftClicked => 1.try_into(),
DialogMsg::RightClicked => 2.try_into(),
} }
} }
} }

View File

@ -11,8 +11,8 @@ use super::{theme, Button, ButtonMsg, Loader, LoaderMsg};
pub enum HoldToConfirmMsg<T> { pub enum HoldToConfirmMsg<T> {
Content(T), Content(T),
Cancelled,
Confirmed, Confirmed,
Cancelled,
} }
pub struct HoldToConfirm<T> { pub struct HoldToConfirm<T> {
@ -36,6 +36,10 @@ where
pad: Pad::with_background(theme::BG), pad: Pad::with_background(theme::BG),
} }
} }
pub fn inner(&self) -> &T {
self.content.inner()
}
} }
impl<T> Component for HoldToConfirm<T> impl<T> Component for HoldToConfirm<T>

View File

@ -10,9 +10,9 @@ pub enum DialogMsg<T, L, R> {
} }
pub struct Dialog<T, L, R> { pub struct Dialog<T, L, R> {
pub content: Child<T>, content: Child<T>,
pub left: Child<L>, left: Child<L>,
pub right: Child<R>, right: Child<R>,
} }
impl<T, L, R> Dialog<T, L, R> impl<T, L, R> Dialog<T, L, R>
@ -28,6 +28,10 @@ where
right: Child::new(right), right: Child::new(right),
} }
} }
pub fn inner(&self) -> &T {
self.content.inner()
}
} }
impl<T, L, R> Component for Dialog<T, L, R> impl<T, L, R> Component for Dialog<T, L, R>

View File

@ -23,6 +23,10 @@ where
content: Child::new(content), content: Child::new(content),
} }
} }
pub fn inner(&self) -> &T {
&self.content.inner()
}
} }
impl<T, U> Component for Frame<T, U> impl<T, U> Component for Frame<T, U>

View File

@ -64,6 +64,10 @@ impl MnemonicInput for Bip39Input {
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.textbox.is_empty() self.textbox.is_empty()
} }
fn mnemonic(&self) -> Option<&'static str> {
self.suggested_word
}
} }
impl Component for Bip39Input { impl Component for Bip39Input {

View File

@ -1,3 +1,5 @@
use core::ops::Deref;
use crate::ui::{ use crate::ui::{
component::{Child, Component, Event, EventCtx, Label, Maybe}, component::{Child, Component, Event, EventCtx, Label, Maybe},
geometry::{Alignment, Grid, Rect}, geometry::{Alignment, Grid, Rect},
@ -13,9 +15,9 @@ pub enum MnemonicKeyboardMsg {
Confirmed, Confirmed,
} }
pub struct MnemonicKeyboard<T> { pub struct MnemonicKeyboard<T, U> {
/// Initial prompt, displayed on empty input. /// Initial prompt, displayed on empty input.
prompt: Child<Maybe<Label<&'static [u8]>>>, prompt: Child<Maybe<Label<U>>>,
/// Backspace button. /// Backspace button.
back: Child<Maybe<Button<&'static [u8]>>>, back: Child<Maybe<Button<&'static [u8]>>>,
/// Input area, acting as the auto-complete and confirm button. /// Input area, acting as the auto-complete and confirm button.
@ -24,11 +26,12 @@ pub struct MnemonicKeyboard<T> {
keys: [Child<Button<&'static [u8]>>; MNEMONIC_KEY_COUNT], keys: [Child<Button<&'static [u8]>>; MNEMONIC_KEY_COUNT],
} }
impl<T> MnemonicKeyboard<T> impl<T, U> MnemonicKeyboard<T, U>
where where
T: MnemonicInput, T: MnemonicInput,
U: Deref<Target = [u8]>,
{ {
pub fn new(input: T, prompt: &'static [u8]) -> Self { pub fn new(input: T, prompt: U) -> Self {
Self { Self {
prompt: Child::new(Maybe::visible( prompt: Child::new(Maybe::visible(
theme::BG, theme::BG,
@ -75,11 +78,16 @@ where
self.back self.back
.mutate(ctx, |ctx, b| b.show_if(ctx, !prompt_visible)); .mutate(ctx, |ctx, b| b.show_if(ctx, !prompt_visible));
} }
pub fn mnemonic(&self) -> Option<&'static str> {
self.input.inner().inner().mnemonic()
}
} }
impl<T> Component for MnemonicKeyboard<T> impl<T, U> Component for MnemonicKeyboard<T, U>
where where
T: MnemonicInput, T: MnemonicInput,
U: Deref<Target = [u8]>,
{ {
type Msg = MnemonicKeyboardMsg; type Msg = MnemonicKeyboardMsg;
@ -158,6 +166,7 @@ pub trait MnemonicInput: Component<Msg = MnemonicInputMsg> {
fn on_key_click(&mut self, ctx: &mut EventCtx, key: usize); fn on_key_click(&mut self, ctx: &mut EventCtx, key: usize);
fn on_backspace_click(&mut self, ctx: &mut EventCtx); fn on_backspace_click(&mut self, ctx: &mut EventCtx);
fn is_empty(&self) -> bool; fn is_empty(&self) -> bool;
fn mnemonic(&self) -> Option<&'static str>;
} }
pub enum MnemonicInputMsg { pub enum MnemonicInputMsg {
@ -167,7 +176,7 @@ pub enum MnemonicInputMsg {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for MnemonicKeyboard<T> { impl<T, U> crate::trace::Trace for MnemonicKeyboard<T, U> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("MnemonicKeyboard"); t.open("MnemonicKeyboard");
t.close(); t.close();

View File

@ -100,6 +100,10 @@ impl PassphraseKeyboard {
self.back.mutate(ctx, |ctx, b| b.enable(ctx)); self.back.mutate(ctx, |ctx, b| b.enable(ctx));
} }
} }
pub fn passphrase(&self) -> &str {
self.input.inner().textbox.content()
}
} }
impl Component for PassphraseKeyboard { impl Component for PassphraseKeyboard {

View File

@ -81,6 +81,10 @@ impl MnemonicInput for Slip39Input {
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.textbox.is_empty() self.textbox.is_empty()
} }
fn mnemonic(&self) -> Option<&'static str> {
self.final_word
}
} }
impl Component for Slip39Input { impl Component for Slip39Input {

View File

@ -14,7 +14,7 @@ pub use dialog::{Dialog, DialogLayout, DialogMsg};
pub use frame::Frame; pub use frame::Frame;
pub use keyboard::{ pub use keyboard::{
bip39::Bip39Input, bip39::Bip39Input,
mnemonic::{MnemonicKeyboard, MnemonicKeyboardMsg}, mnemonic::{MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg},
passphrase::{PassphraseKeyboard, PassphraseKeyboardMsg}, passphrase::{PassphraseKeyboard, PassphraseKeyboardMsg},
pin::{PinKeyboard, PinKeyboardMsg}, pin::{PinKeyboard, PinKeyboardMsg},
slip39::Slip39Input, slip39::Slip39Input,

View File

@ -1,109 +1,124 @@
use core::convert::{TryFrom, TryInto}; use core::{convert::TryInto, ops::Deref};
use crate::{ use crate::{
error::Error, error::Error,
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr}, micropython::{buffer::Buffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
ui::{ ui::{
component::{base::ComponentExt, text::paragraphs::Paragraphs, FormattedText}, component::{
layout::obj::LayoutObj, base::ComponentExt,
paginated::{PageMsg, Paginate},
text::paragraphs::Paragraphs,
Component,
},
layout::{
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED},
},
}, },
util, util,
}; };
use super::{ use super::{
component::{ component::{
Bip39Input, Button, ButtonMsg, DialogMsg, Frame, HoldToConfirm, HoldToConfirmMsg, Bip39Input, Button, ButtonMsg, Dialog, DialogMsg, Frame, HoldToConfirm, HoldToConfirmMsg,
MnemonicKeyboard, MnemonicKeyboardMsg, PassphraseKeyboard, PassphraseKeyboardMsg, MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, PassphraseKeyboard,
PinKeyboard, PinKeyboardMsg, Slip39Input, SwipePage, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Slip39Input, SwipePage,
}, },
theme, theme,
}; };
impl<T> TryFrom<DialogMsg<T, ButtonMsg, ButtonMsg>> for Obj impl<T, U> ComponentMsgObj for Dialog<T, Button<U>, Button<U>>
where where
Obj: TryFrom<T>, T: ComponentMsgObj,
Error: From<<Obj as TryFrom<T>>::Error>, U: AsRef<[u8]>,
{ {
type Error = Error; fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
fn try_from(val: DialogMsg<T, ButtonMsg, ButtonMsg>) -> Result<Self, Self::Error> { DialogMsg::Content(c) => Ok(self.inner().msg_try_into_obj(c)?),
match val { DialogMsg::Left(ButtonMsg::Clicked) => Ok(CANCELLED.as_obj()),
DialogMsg::Content(c) => Ok(c.try_into()?), DialogMsg::Right(ButtonMsg::Clicked) => Ok(CONFIRMED.as_obj()),
DialogMsg::Left(ButtonMsg::Clicked) => 1.try_into(),
DialogMsg::Right(ButtonMsg::Clicked) => 2.try_into(),
_ => Ok(Obj::const_none()), _ => Ok(Obj::const_none()),
} }
} }
} }
impl<T> TryFrom<HoldToConfirmMsg<T>> for Obj impl<T> ComponentMsgObj for HoldToConfirm<T>
where where
Obj: TryFrom<T>, T: ComponentMsgObj,
Error: From<<Obj as TryFrom<T>>::Error>,
{ {
type Error = Error; fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
fn try_from(val: HoldToConfirmMsg<T>) -> Result<Self, Self::Error> { HoldToConfirmMsg::Content(c) => Ok(self.inner().msg_try_into_obj(c)?),
match val { HoldToConfirmMsg::Confirmed => Ok(CONFIRMED.as_obj()),
HoldToConfirmMsg::Content(c) => Ok(c.try_into()?), HoldToConfirmMsg::Cancelled => Ok(CANCELLED.as_obj()),
HoldToConfirmMsg::Confirmed => 1.try_into(),
HoldToConfirmMsg::Cancelled => 2.try_into(),
} }
} }
} }
impl TryFrom<PinKeyboardMsg> for Obj { impl<T> ComponentMsgObj for PinKeyboard<T>
type Error = Error; where
T: Deref<Target = [u8]>,
fn try_from(val: PinKeyboardMsg) -> Result<Self, Self::Error> { {
match val { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
PinKeyboardMsg::Confirmed => 1.try_into(), match msg {
PinKeyboardMsg::Cancelled => 2.try_into(), PinKeyboardMsg::Confirmed => self.pin().try_into(),
PinKeyboardMsg::Cancelled => Ok(CANCELLED.as_obj()),
} }
} }
} }
impl TryFrom<MnemonicKeyboardMsg> for Obj { impl ComponentMsgObj for PassphraseKeyboard {
type Error = Error; fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
fn try_from(val: MnemonicKeyboardMsg) -> Result<Self, Self::Error> { PassphraseKeyboardMsg::Confirmed => self.passphrase().try_into(),
match val { PassphraseKeyboardMsg::Cancelled => Ok(CANCELLED.as_obj()),
MnemonicKeyboardMsg::Confirmed => Ok(Obj::const_true()),
} }
} }
} }
impl TryFrom<PassphraseKeyboardMsg> for Obj { impl<T, U> ComponentMsgObj for MnemonicKeyboard<T, U>
type Error = Error; where
T: MnemonicInput,
fn try_from(val: PassphraseKeyboardMsg) -> Result<Self, Self::Error> { U: Deref<Target = [u8]>,
match val { {
PassphraseKeyboardMsg::Confirmed => Ok(Obj::const_true()), fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
PassphraseKeyboardMsg::Cancelled => Ok(Obj::const_none()), match msg {
MnemonicKeyboardMsg::Confirmed => {
if let Some(word) = self.mnemonic() {
word.try_into()
} else {
panic!("invalid mnemonic")
}
}
} }
} }
} }
#[no_mangle] impl<T, U> ComponentMsgObj for Frame<T, U>
extern "C" fn ui_layout_new_example(_param: Obj) -> Obj { where
let block = move || { T: ComponentMsgObj,
let layout = LayoutObj::new(HoldToConfirm::new( U: AsRef<[u8]>,
FormattedText::new::<theme::TTDefaultText>( {
"Testing text layout, with some text, and some more text. And {param}", fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
) self.inner().msg_try_into_obj(msg)
.with(b"param", b"parameters!"), }
))?;
Ok(layout.into())
};
unsafe { util::try_or_raise(block) }
} }
#[no_mangle] impl<T, U> ComponentMsgObj for SwipePage<T, U>
extern "C" fn ui_layout_new_confirm_action( where
n_args: usize, T: Component + Paginate,
args: *const Obj, U: Component<Msg = bool>,
kwargs: *const Map, {
) -> Obj { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
PageMsg::Content(_) => Err(Error::TypeError),
PageMsg::Controls(true) => Ok(CONFIRMED.as_obj()),
PageMsg::Controls(false) => Ok(CANCELLED.as_obj()),
}
}
}
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
@ -144,8 +159,7 @@ extern "C" fn ui_layout_new_confirm_action(
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
#[no_mangle] extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
extern "C" fn ui_layout_new_pin(n_args: usize, args: *const Obj, kwargs: *const Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let subprompt: Buffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?; let subprompt: Buffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
@ -160,8 +174,7 @@ extern "C" fn ui_layout_new_pin(n_args: usize, args: *const Obj, kwargs: *const
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
#[no_mangle] extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
extern "C" fn ui_layout_new_passphrase(n_args: usize, args: *const Obj, kwargs: *const Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let _prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let _prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let _max_len: u32 = kwargs.get(Qstr::MP_QSTR_max_len)?.try_into()?; let _max_len: u32 = kwargs.get(Qstr::MP_QSTR_max_len)?.try_into()?;
@ -171,41 +184,88 @@ extern "C" fn ui_layout_new_passphrase(n_args: usize, args: *const Obj, kwargs:
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
#[no_mangle] extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
extern "C" fn ui_layout_new_bip39(n_args: usize, args: *const Obj, kwargs: *const Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let _prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new( let obj = LayoutObj::new(MnemonicKeyboard::new(Bip39Input::new(), prompt).into_child())?;
MnemonicKeyboard::new(Bip39Input::new(), b"Type word 11 of 12").into_child(), Ok(obj.into())
)?; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(MnemonicKeyboard::new(Slip39Input::new(), prompt).into_child())?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} }
#[no_mangle] #[no_mangle]
extern "C" fn ui_layout_new_slip39(n_args: usize, args: *const Obj, kwargs: *const Map) -> Obj { pub static mp_module_trezorui2: Module = obj_module! {
let block = move |_args: &[Obj], kwargs: &Map| { Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
let _prompt: Buffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new( /// CONFIRMED: object
MnemonicKeyboard::new(Slip39Input::new(), b"Type word 13 of 20").into_child(), Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(),
)?;
Ok(obj.into()) /// CANCELLED: object
}; Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
} /// def confirm_action(
/// *,
/// title: str,
/// action: str | None = None,
/// description: str | None = None,
/// verb: str | None = None,
/// verb_cancel: str | None = None,
/// hold: bool | None = None,
/// reverse: bool = False,
/// ) -> object:
/// """Confirm action."""
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
/// def request_pin(
/// *,
/// prompt: str,
/// subprompt: str | None = None,
/// allow_cancel: bool = True,
/// warning: str | None = None,
/// ) -> str | object:
/// """Request pin on device."""
Qstr::MP_QSTR_request_pin => obj_fn_kw!(0, new_request_pin).as_obj(),
/// def request_passphrase(
/// *,
/// prompt: str,
/// max_len: int,
/// ) -> str | object:
/// """Passphrase input keyboard."""
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(),
/// def request_bip39(
/// *,
/// prompt: str,
/// ) -> str:
/// """BIP39 word input keyboard."""
Qstr::MP_QSTR_request_bip39 => obj_fn_kw!(0, new_request_bip39).as_obj(),
/// def request_slip39(
/// *,
/// prompt: str,
/// ) -> str:
/// """SLIP39 word input keyboard."""
Qstr::MP_QSTR_request_slip39 => obj_fn_kw!(0, new_request_slip39).as_obj(),
};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
trace::Trace, trace::Trace,
ui::{ ui::{
component::Component, component::{Component, FormattedText},
geometry::Rect, geometry::Rect,
model_tt::{ model_tt::constant,
component::{Button, Dialog},
constant,
},
}, },
}; };

View File

@ -1,8 +1,10 @@
from typing import * from typing import *
CONFIRMED: object
CANCELLED: object
# extmod/rustmods/modtrezorui2.c # rust/src/ui/model_t1/layout.rs
def layout_new_confirm_action( def confirm_action(
*, *,
title: str, title: str,
action: str | None = None, action: str | None = None,
@ -12,55 +14,66 @@ def layout_new_confirm_action(
hold: bool | None = None, hold: bool | None = None,
reverse: bool = False, reverse: bool = False,
) -> object: ) -> object:
"""Example layout.""" """Confirm action."""
# extmod/rustmods/modtrezorui2.c # rust/src/ui/model_t1/layout.rs
def layout_new_example(text: str) -> object: def confirm_text(
"""Example layout."""
# extmod/rustmods/modtrezorui2.c
def layout_new_pin(
*,
prompt: str,
subprompt: str,
allow_cancel: bool,
warning: str | None,
) -> object:
"""PIN keyboard."""
# extmod/rustmods/modtrezorui2.c
def layout_new_passphrase(
*,
prompt: str,
max_len: int,
) -> object:
"""Passphrase keyboard."""
# extmod/rustmods/modtrezorui2.c
def layout_new_bip39(
*,
prompt: str,
) -> object:
"""BIP39 keyboard."""
# extmod/rustmods/modtrezorui2.c
def layout_new_slip39(
*,
prompt: str,
) -> object:
"""BIP39 keyboard."""
# extmod/rustmods/modtrezorui2.c
def layout_new_confirm_text(
*, *,
title: str, title: str,
data: str, data: str,
description: str | None, description: str | None,
) -> object: ) -> object:
"""Example layout.""" """Confirm text."""
CONFIRMED: object
CANCELLED: object
# rust/src/ui/model_tt/layout.rs
def confirm_action(
*,
title: str,
action: str | None = None,
description: str | None = None,
verb: str | None = None,
verb_cancel: str | None = None,
hold: bool | None = None,
reverse: bool = False,
) -> object:
"""Confirm action."""
# rust/src/ui/model_tt/layout.rs
def request_pin(
*,
prompt: str,
subprompt: str | None = None,
allow_cancel: bool = True,
warning: str | None = None,
) -> str | object:
"""Request pin on device."""
# rust/src/ui/model_tt/layout.rs
def request_passphrase(
*,
prompt: str,
max_len: int,
) -> str | object:
"""Passphrase input keyboard."""
# rust/src/ui/model_tt/layout.rs
def request_bip39(
*,
prompt: str,
) -> str:
"""BIP39 word input keyboard."""
# rust/src/ui/model_tt/layout.rs
def request_slip39(
*,
prompt: str,
) -> str:
"""SLIP39 word input keyboard."""

View File

@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
from trezor import io, log, loop, ui, wire, workflow from trezor import io, log, loop, ui, wire, workflow
from trezor.enums import ButtonRequestType from trezor.enums import ButtonRequestType
from trezorui2 import layout_new_confirm_action, layout_new_confirm_text import trezorui2
from .common import interact from .common import interact
@ -84,7 +84,7 @@ async def confirm_action(
result = await interact( result = await interact(
ctx, ctx,
_RustLayout( _RustLayout(
layout_new_confirm_action( trezorui2.confirm_action(
title=title.upper(), title=title.upper(),
action=action, action=action,
description=description, description=description,
@ -97,7 +97,7 @@ async def confirm_action(
br_type, br_type,
br_code, br_code,
) )
if result == 1: if result is not trezorui2.CONFIRMED:
raise exc raise exc
@ -114,7 +114,7 @@ async def confirm_text(
result = await interact( result = await interact(
ctx, ctx,
_RustLayout( _RustLayout(
layout_new_confirm_text( trezorui2.confirm_text(
title=title.upper(), title=title.upper(),
data=data, data=data,
description=description, description=description,
@ -123,7 +123,7 @@ async def confirm_text(
br_type, br_type,
br_code, br_code,
) )
if result == 0: if result is not trezorui2.CONFIRMED:
raise wire.ActionCancelled raise wire.ActionCancelled

View File

@ -3,15 +3,8 @@ from typing import TYPE_CHECKING
from trezor import io, log, loop, ui, wire, workflow from trezor import io, log, loop, ui, wire, workflow
from trezor.enums import ButtonRequestType from trezor.enums import ButtonRequestType
from trezorui2 import ( import trezorui2
layout_new_bip39,
layout_new_confirm_action,
layout_new_passphrase,
layout_new_pin,
layout_new_slip39,
)
from ...components.tt import passphrase, pin
from ...constants.tt import MONO_ADDR_PER_LINE from ...constants.tt import MONO_ADDR_PER_LINE
from ..common import button_request, interact from ..common import button_request, interact
@ -99,7 +92,7 @@ async def confirm_action(
result = await interact( result = await interact(
ctx, ctx,
_RustLayout( _RustLayout(
layout_new_confirm_action( trezorui2.confirm_action(
title=title.upper(), title=title.upper(),
action=action, action=action,
description=description, description=description,
@ -111,7 +104,7 @@ async def confirm_action(
br_type, br_type,
br_code, br_code,
) )
if result is not True: if result is not trezorui2.CONFIRMED:
raise exc raise exc
@ -454,10 +447,10 @@ async def request_passphrase_on_device(ctx: wire.GenericContext, max_len: int) -
) )
keyboard = _RustLayout( keyboard = _RustLayout(
layout_new_passphrase(prompt="Enter passphrase", max_len=max_len) trezorui2.request_passphrase(prompt="Enter passphrase", max_len=max_len)
) )
result = await ctx.wait(keyboard) result = await ctx.wait(keyboard)
if result is passphrase.CANCELLED: if result is trezorui2.CANCELLED:
raise wire.ActionCancelled("Passphrase entry cancelled") raise wire.ActionCancelled("Passphrase entry cancelled")
assert isinstance(result, str) assert isinstance(result, str)
@ -480,7 +473,7 @@ async def request_pin_on_device(
subprompt = f"{attempts_remaining} tries left" subprompt = f"{attempts_remaining} tries left"
dialog = _RustLayout( dialog = _RustLayout(
layout_new_pin( trezorui2.request_pin(
prompt=prompt, prompt=prompt,
subprompt=subprompt, subprompt=subprompt,
allow_cancel=allow_cancel, allow_cancel=allow_cancel,
@ -489,7 +482,7 @@ async def request_pin_on_device(
) )
while True: while True:
result = await ctx.wait(dialog) result = await ctx.wait(dialog)
if result is pin.CANCELLED: if result is trezorui2.CANCELLED:
raise wire.PinCancelled raise wire.PinCancelled
assert isinstance(result, str) assert isinstance(result, str)
return result return result
@ -500,11 +493,15 @@ async def request_word(
) -> str: ) -> str:
if is_slip39: if is_slip39:
keyboard: Any = _RustLayout( keyboard: Any = _RustLayout(
layout_new_bip39(prompt=f"Type word {word_index + 1} of {word_count}:") trezorui2.request_bip39(
prompt=f"Type word {word_index + 1} of {word_count}:"
)
) )
else: else:
keyboard = _RustLayout( keyboard = _RustLayout(
layout_new_slip39(prompt=f"Type word {word_index + 1} of {word_count}:") trezorui2.request_slip39(
prompt=f"Type word {word_index + 1} of {word_count}:"
)
) )
word: str = await ctx.wait(keyboard) word: str = await ctx.wait(keyboard)

View File

@ -8,6 +8,7 @@ from pathlib import Path
CORE_DIR = Path(__file__).resolve().parent.parent CORE_DIR = Path(__file__).resolve().parent.parent
EXTMOD_PATH = CORE_DIR / "embed" / "extmod" EXTMOD_PATH = CORE_DIR / "embed" / "extmod"
RUSTMOD_PATH = CORE_DIR / "embed" / "rust" / "src"
MOCKS_PATH = CORE_DIR / "mocks" / "generated" MOCKS_PATH = CORE_DIR / "mocks" / "generated"
COMMENT_PREFIX = "/// " COMMENT_PREFIX = "/// "
@ -101,6 +102,37 @@ def build_module(mod_file, dest):
store_to_file(dest, split_to_parts(l, mod_desc)) store_to_file(dest, split_to_parts(l, mod_desc))
def build_rsmodule(mod_file, dest):
global current_indent
global current_class
global current_package
assert mod_file.suffix == ".rs"
start_prefix = "pub static mp_module_"
comment_prefix = f" {COMMENT_PREFIX}"
in_module = False
current_indent = 0
current_class = None
mod_desc = str(mod_file.relative_to(CORE_DIR / "embed"))
for l in open(mod_file):
if l.startswith(start_prefix):
in_module = True
current_package = l[len(start_prefix) : ].split(":")[0]
elif l.startswith("}"):
in_module = False
if not in_module:
continue
if not l.startswith(comment_prefix):
continue
l = l[len(comment_prefix) :]
store_to_file(dest, split_to_parts(l, mod_desc))
def place_symlinks(dest): def place_symlinks(dest):
# make symlinks for the non-generated files # make symlinks for the non-generated files
for pyi in MOCKS_PATH.glob("../*.pyi"): for pyi in MOCKS_PATH.glob("../*.pyi"):
@ -111,6 +143,8 @@ def place_symlinks(dest):
def build_directory(dest): def build_directory(dest):
for modfile in sorted(EXTMOD_PATH.glob("**/mod*.[ch]")): for modfile in sorted(EXTMOD_PATH.glob("**/mod*.[ch]")):
build_module(modfile, dest) build_module(modfile, dest)
for modfile in sorted(RUSTMOD_PATH.glob("**/*.rs")):
build_rsmodule(modfile, dest)
place_symlinks(dest) place_symlinks(dest)