Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make DialogFileBrowserOptions::new() use native initialization function #92

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Added

- `flipperzero::dialogs::DialogFileBrowserOptions`

### Changed

- `flipperzero::dialogs::DialogFileBrowserOptions` now uses native initialization function.

### Removed

## [0.12.0]
Expand Down
114 changes: 92 additions & 22 deletions crates/flipperzero/src/dialogs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use alloc::ffi::CString;

use core::ffi::{c_void, CStr};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::{self, NonNull};

JarvisCraft marked this conversation as resolved.
Show resolved Hide resolved
use flipperzero_sys as sys;
Expand All @@ -25,6 +26,7 @@ pub struct DialogMessage<'a> {
}

/// A dialog file browser options.
#[repr(transparent)]
JarvisCraft marked this conversation as resolved.
Show resolved Hide resolved
pub struct DialogFileBrowserOptions<'a> {
JarvisCraft marked this conversation as resolved.
Show resolved Hide resolved
data: sys::DialogsFileBrowserOptions,
_phantom: PhantomData<&'a ()>,
Expand Down Expand Up @@ -215,40 +217,108 @@ impl<'a> Default for DialogFileBrowserOptions<'a> {
}

impl<'a> DialogFileBrowserOptions<'a> {
/// Creates a new dialog file browser options and initializes to default values.
pub fn new() -> Self {
// SAFETY: the string is a valid UTF-8
unsafe { Self::with_extension(c"*") }
}

/// Creates a new dialog file browser options and initializes to default values.
///
/// # Safety
///
/// `extension` should be a valid UTF-8 string
///
/// # Compatibility
///
/// This function's signature may change in the future to make it safe.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use flipperzero::dialogs::DialogFileBrowserOptions;
/// let options = DialogFileBrowserOptions::new(c"*");
/// ```
///
/// ## Lifetime covariance:
///
/// Even if `'static` lifetime is involved in the creation of options,
/// the resulting lifetime will be the most applicable one:
///
/// ```
/// # use core::ffi::CStr;
/// # use flipperzero::dialogs::DialogFileBrowserOptions;
/// // has `'static` lifetime
/// const EXTENSION: &CStr = c"txt";
/// // has "local" lifetime, aka `'a`
/// let base_path_bytes = [b'/', b'r', b'o', b'o', b't'];
/// let base_path = CStr::from_bytes_with_nul(&base_path_bytes).unwrap();
/// // the most appropriate lifetime `'a` is used
/// // SAFETY: `EXTENSION` is a valid UTF-8 string
/// let mut options = unsafe { DialogFileBrowserOptions::new(EXTENSION) }
/// .set_base_path(base_path);
/// ```
///
/// Still this should not allow the options to outlive its components:
///
/// ```compile_fail
/// # use core::ffi::CStr;
/// # use flipperzero::dialogs::DialogFileBrowserOptions;
/// # use flipperzero_sys::{cstr, DialogsFileBrowserOptions};
/// const EXTENSION: &CStr = cstr!("*");
/// // SAFETY: `EXTENSION` is a valid UTF-8 string
/// let mut options = unsafe { DialogFileBrowserOptions::new(EXTENSION) };
/// {
/// let base_path_bytes = [b'/', b'r', b'o', b'o', b't'];
/// let base_path = CStr::from_bytes_with_nul(&base_path_bytes).unwrap();
/// options = options.set_base_path(base_path);
/// }
/// ```
pub unsafe fn with_extension(extension: &'a CStr) -> Self {
let mut options = MaybeUninit::<sys::DialogsFileBrowserOptions>::uninit();
let uninit_options = options.as_mut_ptr();
let extension = extension.as_ptr();
// TODO: as for now, we stick to default (NULL) icon,
// although we may want to make it customizable via this function's parameter
// once there are safe Icon-related APIs
let icon = ptr::null();
// SAFETY: all pointers are valid (`icon` is allowed to be NULL)
// and options is intentionally uninitialized
// since it is the called function's job to do it
unsafe { sys::dialog_file_browser_set_basic_options(uninit_options, extension, icon) };
Self {
// default values from sys::dialog_file_browser_set_basic_options()
data: sys::DialogsFileBrowserOptions {
extension: c"*".as_ptr(),
base_path: ptr::null(),
skip_assets: true,
hide_dot_files: false,
icon: ptr::null(),
hide_ext: true,
item_loader_callback: None,
item_loader_context: ptr::null_mut(),
},
// SAFETY: data has just been initialized fully
// as guaranteed by the previously called function's contract
data: unsafe { options.assume_init() },
_phantom: PhantomData,
}
}

/// Set file extension to be offered for selection.
pub fn set_extension(
mut self,
// FIXME: this is unsound for non-UTF8 string
extension: &'a CStr,
) -> Self {
///
/// # Safety
///
/// `extension` should be a valid UTF-8 string
///
/// # Compatibility
///
/// This function's signature may change in the future to make it safe.
pub unsafe fn set_extension(mut self, extension: &'a CStr) -> Self {
self.data.extension = extension.as_ptr();
self
}

/// Set root folder path for navigation with back key.
pub fn set_base_path(
mut self,
// FIXME: this is unsound for non-UTF8 string
base_path: &'a CStr,
) -> Self {
///
/// # Safety
///
/// `base_path` should be a valid UTF-8 string
///
/// # Compatibility
///
/// This function's signature may change in the future to make it safe.
pub unsafe fn set_base_path(mut self, base_path: &'a CStr) -> Self {
self.data.base_path = base_path.as_ptr();
self
}
Expand Down
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading