Skip to content

Commit

Permalink
Update generator (#191)
Browse files Browse the repository at this point in the history
* codegen: Handle return values of u[8,16,32] and boolean type

* codegen: Update wrapper creation to handle return values

* codegen: How to handle calls for getters?

* codegen: Update and handle tests for getters

* Remove manual getters from custom code, now they're generated

* codegen: Add comments and renaming variables for clarity

* codegen: Improve? matching return types

* label: Comment out get_recolor interface

* codegen: Don't clone return value, we can reference it

* codegen: Don't use extra variable, we can check self.ret to know if there's a return value

* widgets: Remove commented out interfaces
  • Loading branch information
C47D authored Jan 18, 2025
1 parent 941c7c5 commit bdc49f7
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 46 deletions.
195 changes: 160 additions & 35 deletions lvgl-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,23 +129,41 @@ impl Rusty for LvFunc {
});
}

// We don't deal with methods that return types yet
if self.ret.is_some() {
return Err(WrapperError::Skip);
}
// Handle return values
let return_type = match self.ret {
// function returns void
None => quote!(()),
// function returns something
_ => {
let return_value: &LvType = self.ret.as_ref().unwrap();
match return_value.literal_name.as_str() {
"bool" => quote!(bool),
"u32" => quote!(u32),
"i32" => quote!(i32),
"u16" => quote!(u16),
"i16" => quote!(i16),
"u8" => quote!(u8),
"i8" => quote!(i8),
_ => return Err(WrapperError::Skip)
}
}
};

// Make sure all arguments can be generated, skip the first arg (self)!
for arg in self.args.iter().skip(1) {
arg.code(self)?;
}

// Generate the arguments being passed into the Rust 'wrapper'
//
// - Iif the first argument (of the C function) is const then we require a &self immutable reference, otherwise an &mut self reference
// - The arguments will be appended to the accumulator (args_accumulator) as they are generated in the closure
let args_decl = self
.args
.iter()
.enumerate()
.fold(quote!(), |args, (i, arg)| {
// if first arg is `const`, then it should be immutable
let next_arg = if i == 0 {
.fold(quote!(), |args_accumulator, (arg_idx, arg)| {
let next_arg = if arg_idx == 0 {
if arg.get_type().is_const() {
quote!(&self)
} else {
Expand All @@ -154,14 +172,14 @@ impl Rusty for LvFunc {
} else {
arg.code(self).unwrap()
};
if args.is_empty() {
quote! {
#next_arg
}
} else {
quote! {
#args, #next_arg
}

// If the accummulator is empty then we call quote! only with the next_arg content
if args_accumulator.is_empty() {
quote! {#next_arg}
}
// Otherwise we append next_arg at the end of the accumulator
else {
quote! {#args_accumulator, #next_arg}
}
});

Expand Down Expand Up @@ -189,37 +207,55 @@ impl Rusty for LvFunc {
}
});

let args_call = self
// Generate the arguments being passed into the FFI interface
//
// - The first argument will be always self.core.raw().as_mut() (see quote! when arg_idx == 0), it's most likely a pointer to lv_obj_t
// TODO: When handling getters this should be self.raw().as_ptr() instead, this also requires updating args_decl
// - The arguments will be appended to the accumulator (args_accumulator) as they are generated in the closure
let ffi_args = self
.args
.iter()
.enumerate()
.fold(quote!(), |args, (i, arg)| {
// if first arg is `const`, then it should be immutable
let next_arg = if i == 0 {
.fold(quote!(), |args_accumulator, (arg_idx, arg)| {
let next_arg = if arg_idx == 0 {
quote!(self.core.raw().as_mut())
} else {
let var = arg.get_value_usage();
quote!(#var)
};
if args.is_empty() {
quote! {
#next_arg
}
} else {
quote! {
#args, #next_arg
}

// If the accummulator is empty then we call quote! only with the next_arg content
if args_accumulator.is_empty() {
quote! {#next_arg}
}
// Otherwise we append next_arg at the end of the accumulator
else {
quote! {#args_accumulator, #next_arg}
}
});

// TODO: Handle methods that return types
// NOTE: When the function returns something we can 'avoid' placing an Ok() at the end.
let explicit_ok = if return_type.is_empty() {
quote!(Ok(()))
} else {
quote!()
};

// Append a semicolon at the end of the unsafe code only if there's no return value.
// Otherwise we should remove it
let optional_semicolon= match self.ret {
None => quote!(;),
_ => quote!()
};

Ok(quote! {
pub fn #func_name(#args_decl) -> crate::LvResult<()> {
pub fn #func_name(#args_decl) -> #return_type {
#args_processing
unsafe {
lvgl_sys::#original_func_name(#args_call);
lvgl_sys::#original_func_name(#ffi_args)#optional_semicolon
}
Ok(())

#explicit_ok
}
})
}
Expand Down Expand Up @@ -555,11 +591,10 @@ mod test {

let code = arc_set_bg_end_angle.code(&arc_widget).unwrap();
let expected_code = quote! {
pub fn set_bg_end_angle(&mut self, end: u16) -> crate::LvResult<()> {
pub fn set_bg_end_angle(&mut self, end: u16) -> () {
unsafe {
lvgl_sys::lv_arc_set_bg_end_angle(self.core.raw().as_mut(), end);
}
Ok(())
}
};

Expand Down Expand Up @@ -587,16 +622,106 @@ mod test {
let code = label_set_text.code(&parent_widget).unwrap();
let expected_code = quote! {

pub fn set_text(&mut self, text: &cstr_core::CStr) -> crate::LvResult<()> {
pub fn set_text(&mut self, text: &cstr_core::CStr) -> () {
unsafe {
lvgl_sys::lv_label_set_text(
self.core.raw().as_mut(),
text.as_ptr()
);
}
}

};

assert_eq!(code.to_string(), expected_code.to_string());
}

#[test]
fn generate_method_wrapper_for_void_return() {
let bindgen_code = quote! {
extern "C" {
#[doc = " Set a new text for a label. Memory will be allocated to store the text by the label."]
#[doc = " @param label pointer to a label object"]
#[doc = " @param text '\\0' terminated character string. NULL to refresh with the current text."]
pub fn lv_label_set_text(label: *mut lv_obj_t, text: *const cty::c_char);
}
};
let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap();

let label_set_text = cg.get(0).unwrap().clone();
let parent_widget = LvWidget {
name: "label".to_string(),
methods: vec![],
};

let code = label_set_text.code(&parent_widget).unwrap();
let expected_code = quote! {
pub fn set_text(&mut self, text: &cstr_core::CStr) -> () {
unsafe {
lvgl_sys::lv_label_set_text(
self.core.raw().as_mut(),
text.as_ptr()
);
}
Ok(())
}
};

assert_eq!(code.to_string(), expected_code.to_string());
}

#[test]
fn generate_method_wrapper_for_boolean_return() {
let bindgen_code = quote! {
extern "C" {
pub fn lv_label_get_recolor(label: *mut lv_obj_t) -> bool;
}
};
let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap();

let label_get_recolor = cg.get(0).unwrap().clone();
let parent_widget = LvWidget {
name: "label".to_string(),
methods: vec![],
};

let code = label_get_recolor.code(&parent_widget).unwrap();
let expected_code = quote! {
pub fn get_recolor(&mut self) -> bool {
unsafe {
lvgl_sys::lv_label_get_recolor(
self.core.raw().as_mut()
)
}
}
};

assert_eq!(code.to_string(), expected_code.to_string());
}

#[test]
fn generate_method_wrapper_for_uint32_return() {
let bindgen_code = quote! {
extern "C" {
pub fn lv_label_get_text_selection_start(label: *mut lv_obj_t) -> u32;
}
};
let cg = CodeGen::load_func_defs(bindgen_code.to_string().as_str()).unwrap();

let label_get_text_selection_start = cg.get(0).unwrap().clone();
let parent_widget = LvWidget {
name: "label".to_string(),
methods: vec![],
};

let code = label_get_text_selection_start.code(&parent_widget).unwrap();
let expected_code = quote! {
pub fn get_text_selection_start(&mut self) -> u32 {
unsafe {
lvgl_sys::lv_label_get_text_selection_start(
self.core.raw().as_mut()
)
}
}
};

assert_eq!(code.to_string(), expected_code.to_string());
Expand Down
5 changes: 0 additions & 5 deletions lvgl/src/widgets/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ impl Arc<'_> {
// }
// Ok(())
// }

/// Gets the current value of the arc
pub fn get_value(&self) -> i32 {
unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) }
}
}
/*
/// The different parts, of an arc object.
Expand Down
5 changes: 0 additions & 5 deletions lvgl/src/widgets/bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ impl Bar<'_> {
lvgl_sys::lv_bar_set_value(self.core.raw().as_mut(), value, anim.into());
}
}

/// Gets the current value of the bar
pub fn get_value(&self) -> i32 {
unsafe { lvgl_sys::lv_bar_get_value(self.core.raw().as_ptr()) }
}
}
/*
/// The different parts, of a bar object.
Expand Down
2 changes: 1 addition & 1 deletion lvgl/src/widgets/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod alloc_imp {
// text.try_into().unwrap()
let text_cstr = CString::new(text.as_ref()).unwrap();
let mut label = Label::new().unwrap();
label.set_text(text_cstr.as_c_str()).unwrap();
label.set_text(text_cstr.as_c_str());
label
}
}
Expand Down

0 comments on commit bdc49f7

Please sign in to comment.