[rust] refactor Notcurses & NcDirect wrapping approach

- remove `Nc` & `NcD` wrappers, to move them to notcurses-rs library.
- update the summary header format for Notcurses and NcDirect
- update docs and examples
This commit is contained in:
joseLuís 2021-05-05 19:33:00 +02:00
parent 73fc4ea935
commit 129e208438
20 changed files with 74 additions and 306 deletions

View File

@ -15,38 +15,26 @@ and the [**C way**](#2-the-c-way). (Or a mix of both).
## 1. The Rust way
Where you use the safely wrapped types, with its methods and constructors,
Where you use the more safely wrapped types, with its methods and constructors,
and painless error handling, like this:
```rust
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::with_flags(NCOPTION_NO_ALTERNATE_SCREEN)?;
let mut nc = Notcurses::with_flags(NCOPTION_NO_ALTERNATE_SCREEN)?;
let plane = nc.stdplane();
plane.putstr("hello world")?;
nc.render()?;
nc.stop()?;
Ok(())
}
```
Specifically `Nc` and `NcD` are safe wrappers over `Notcurses`
and `NcDirect`, respectively.
`Nc` and `NcD` both implement the Drop, AsRef, AsMut, Deref and DerefMut traits.
Their destructors are called automatically at the end of their scope.
Methods are directly implemented for `Notcurses` and `NcDirect`, and are
made automatically available to `Nc` & `NcD`, minus some function overrides,
like their destructors, plus the static methods that have to be recreated.
The rest of the types that allocate, like `NcPlane`, `NcMenu`,
`NcReader`… have no higher level wrapping struct, they don't
implement Drop, so they have to be `*.destroy()`ed manually.
But they do implement methods and use `NcResult` as the return type,
for handling errors in the way we are used to in Rust.
Although you still have to manually call the `stop()` method for `Notcurses`
and `NcDirect` objects, and the `destroy()` method for the rest of types that
allocate, (like `NcPlane`, `NcMenu`…) at the end of their scope, since the Drop
trait is not implemented for any wrapping type in libnotcurses-sys.
For the types that don't allocate, most are based on primitives like `i32`,
`u32`, `u64`… without a name in the C library. In Rust they are type aliased

View File

@ -1,7 +1,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut dm = NcD::new()?;
let dm = NcDirect::new()?;
let (t_rows, t_cols) = dm.dim_yx();
println!("Terminal rows={0}, cols={1}", t_rows, t_cols);

View File

@ -10,7 +10,7 @@ use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut rng = thread_rng();
let mut dm = NcD::new()?;
let mut dm = NcDirect::new()?;
let cols = dm.dim_x();
let rows = dm.dim_y();

View File

@ -7,7 +7,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut dm = NcD::new()?;
let mut dm = NcDirect::new()?;
render_image(&mut dm, NCBLIT_1x1)?;
render_image(&mut dm, NCBLIT_2x1)?;
@ -16,7 +16,7 @@ fn main() -> NcResult<()> {
Ok(())
}
fn render_image(dm: &mut NcD, blit: NcBlitter) -> NcResult<()> {
fn render_image(dm: &mut NcDirect, blit: NcBlitter) -> NcResult<()> {
if let Err(nc_error) = dm.render_image("image-16x16.png", NCALIGN_CENTER, blit, NCSCALE_NONE) {
return Err(NcError::with_msg(
nc_error.int,

View File

@ -1,7 +1,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::without_altscreen()?;
let nc = Notcurses::without_altscreen()?;
let (t_rows, t_cols) = nc.term_dim_yx();
println!("Terminal rows={0}, cols={1}", t_rows, t_cols);
@ -38,5 +38,6 @@ Palette size: {11:?}
println!("{:#?}", pixelgeom);
nc.render()?;
nc.stop()?;
Ok(())
}

View File

@ -6,7 +6,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::with_flags(
let mut nc = Notcurses::with_flags(
NCOPTION_SUPPRESS_BANNERS | NCOPTION_NO_WINCH_SIGHANDLER | NCOPTION_NO_QUIT_SIGHANDLERS,
)?;
@ -32,5 +32,6 @@ fn main() -> NcResult<()> {
println!("\nExiting...");
rsleep![&mut nc, 1];
nc.stop()?;
Ok(())
}

View File

@ -1,7 +1,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::new()?;
let mut nc = Notcurses::new()?;
// get the terminal size in character rows & columns
let (t_rows, t_cols) = nc.term_dim_yx();
@ -59,5 +59,6 @@ fn main() -> NcResult<()> {
rsleep![&mut nc, 3];
nc.stop()?;
Ok(())
}

View File

@ -6,7 +6,7 @@ const WIDTH: u32 = 10;
const HEIGHT: u32 = 10;
fn main() -> NcResult<()> {
let mut nc = Nc::new()?;
let mut nc = Notcurses::new()?;
if !nc.check_pixel_support()? {
return Err(NcError::new_msg("Current terminal doesn't support pixels."));
@ -33,5 +33,6 @@ fn main() -> NcResult<()> {
rsleep![&mut nc, 2];
vframe.destroy();
nc.stop()?;
Ok(())
}

View File

@ -12,7 +12,7 @@ use rand::{distributions::Uniform, Rng};
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::new()?;
let mut nc = Notcurses::new()?;
if !nc.check_pixel_support()? {
return Err(NcError::new_msg("Current terminal doesn't support pixels."));
@ -81,5 +81,6 @@ fn main() -> NcResult<()> {
vframe1.destroy();
vframe4.destroy();
nc.stop()?;
Ok(())
}

View File

@ -3,7 +3,8 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::new()?;
let mut nc = Notcurses::new()?;
let plane = nc.stdplane();
plane.set_scrolling(true);
@ -19,4 +20,6 @@ fn main() -> NcResult<()> {
}
rsleep![&mut nc, 0, 0, 30];
}
// nc.stop()?;
}

View File

@ -3,7 +3,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut dm = NcD::new()?;
let dm = NcDirect::new()?;
let dimy = dm.dim_y();
let dimx = dm.dim_x();

View File

@ -3,7 +3,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut dm = NcD::new()?;
let dm = NcDirect::new()?;
dm.set_fg_rgb8(100, 100, 100)?;
dm.set_bg_rgb8(0xff, 0xff, 0xff)?;

View File

@ -6,7 +6,7 @@
use libnotcurses_sys::*;
fn main() -> NcResult<()> {
let mut nc = Nc::new()?;
let mut nc = Notcurses::new()?;
nc.mouse_enable()?;
let mut demo_items = [
@ -72,7 +72,7 @@ fn main() -> NcResult<()> {
Ok(())
}
fn run_menu(nc: &mut Nc, menu: &mut NcMenu) -> NcResult<()> {
fn run_menu(nc: &mut Notcurses, menu: &mut NcMenu) -> NcResult<()> {
// yellow rectangle
let planeopts = NcPlaneOptions::new_aligned(10, NCALIGN_CENTER, 3, 40);
let stdplane = nc.stdplane();
@ -104,6 +104,7 @@ fn run_menu(nc: &mut Nc, menu: &mut NcMenu) -> NcResult<()> {
'q' => {
menu.destroy()?;
selplane.destroy()?;
nc.stop()?;
return Ok(());
}
NCKEY_ENTER => {
@ -112,6 +113,7 @@ fn run_menu(nc: &mut Nc, menu: &mut NcMenu) -> NcResult<()> {
"Quit" => {
menu.destroy()?;
selplane.destroy()?;
nc.stop()?;
return Ok(());
}
_ => (),

View File

@ -177,7 +177,7 @@ pub use ffi::{
ncdirect_off_styles,
ncdirect_on_styles,
ncdirect_palette_size,
ncdirect_printf_aligned,
//W ncdirect_printf_aligned,
ncdirect_putstr,
ncdirect_raster_frame,
ncdirect_readline,

View File

@ -1,6 +1,6 @@
//! Test `NcCell` methods and associated functions.
use crate::{Nc, NcCell, NcPlane};
use crate::{Notcurses, NcCell, NcPlane};
use serial_test::serial;
@ -10,8 +10,9 @@ fn constructors() -> crate::NcResult<()> {
let _c1 = NcCell::new();
let _c2 = NcCell::with_char7b('C');
let mut nc = Nc::new()?;
let plane = NcPlane::new(&mut nc, 0, 0, 10, 10)?;
let nc = Notcurses::new()?;
let plane = NcPlane::new(nc, 0, 0, 10, 10)?;
let _c3 = NcCell::with_char('௵', plane);
nc.stop()?;
Ok(())
}

View File

@ -1,29 +1,19 @@
//! `NcDirect`
// --- ---------------------------------------------------------
// col 0:
// --------------
// 4 X: wont do
// ~: WIP
// total: 47
// ---------------------------------------------------
// (X) 1 : wont do
//
// col 1: 43
// --------------
// 39 f: ffi function imported by bindgen
// F: ffi function wrapped safely
// 4 r: static function reimplemented in Rust
// (f) 42 : unsafe ffi function exported by bindgen
// (w) 0 : safely wrapped ffi function
// (r) 4 : static function manually reimplemented
//
// col 2: 43
// --------------
// 41 m: impl as an `NcDirect` method
// 2 M: impl for the `NcD` wrapper struct too
//
// col 3:
// --------------
// t: tests done for the ffi or reimplemented funtion
// T: tests done also for the m method
// Ŧ: tests done also for the M method wrapper struct
// --- ---------------------------------------------------------
// (m) 45 : method implemented
// (~) 1 : work in progress
//
// (t) 0 : unit test done for the function
// (T) 0 : unit test done also for the method
// ---------------------------------------------------
// fm ncdirect_bg_default
// fm ncdirect_bg_palindex
// fm ncdirect_bg_rgb
@ -52,7 +42,7 @@
// fm ncdirect_flush
// fm ncdirect_getc
// fm ncdirect_hline_interp
// fM ncdirect_init
// fm ncdirect_init
// fm ncdirect_inputready_fd
// fm ncplane_on_styles
// fm ncplane_off_styles
@ -65,10 +55,7 @@
// fm ncdirect_render_image
// fm ncdirect_rounded_box
// fm ncplane_set_styles
// fM ncdirect_stop
//X ncdirect_styles_off // deprecated
//X ncdirect_styles_on // deprecated
//X ncdirect_styles_set // deprecated
// fm ncdirect_stop
// fm ncdirect_vline_interp
// rm ncdirect_bg_rgb8
// rm ncdirect_fg_rgb8
@ -80,10 +67,8 @@ mod test;
mod methods;
mod reimplemented;
mod wrapper;
pub use reimplemented::*;
pub use wrapper::*;
/// Minimal notcurses instance for styling text.
///

View File

@ -1,67 +0,0 @@
//! `NcD` wrapper struct and traits implementations.
use std::ops::{Deref, DerefMut};
use crate::{raw_wrap, NcDirect, NcResult};
/// Safe wrapper around [NcDirect], minimal notcurses instance for styling text.
#[derive(Debug)]
pub struct NcD<'a> {
pub(crate) raw: &'a mut NcDirect,
}
impl<'a> AsRef<NcDirect> for NcD<'a> {
fn as_ref(&self) -> &NcDirect {
self.raw
}
}
impl<'a> AsMut<NcDirect> for NcD<'a> {
fn as_mut(&mut self) -> &mut NcDirect {
self.raw
}
}
impl<'a> Deref for NcD<'a> {
type Target = NcDirect;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> DerefMut for NcD<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl<'a> Drop for NcD<'a> {
/// Destroys the NcD context.
fn drop(&mut self) {
let _ = self.raw.stop();
}
}
/// # Constructors and methods overriden from NcDirect
impl<'a> NcD<'a> {
// wrap constructors
/// New NcD (without banners).
pub fn new() -> NcResult<Self> {
raw_wrap![NcDirect::new()]
}
/// New NcD, expects `NCOPTION_*` flags.
pub fn with_flags(flags: u64) -> NcResult<Self> {
raw_wrap![NcDirect::with_flags(flags)]
}
// disable destructor
/// Since NcD already implements [Drop](#impl-Drop),
/// this function is made no-op.
pub fn stop(&mut self) -> NcResult<()> {
Ok(())
}
}

View File

@ -22,10 +22,11 @@
//! use libnotcurses_sys::*;
//!
//! fn main() -> NcResult<()> {
//! let mut nc = Nc::with_flags(NCOPTION_NO_ALTERNATE_SCREEN)?;
//! let mut nc = Notcurses::with_flags(NCOPTION_NO_ALTERNATE_SCREEN)?;
//! let plane = nc.stdplane();
//! plane.putstr("hello world")?;
//! nc.render()?;
//! nc.stop()?;
//! Ok(())
//! }
//! ```

View File

@ -1,29 +1,19 @@
//! `Notcurses`
// --- -------------------------------------------------------------------------
// col 0: 3
// --------------
// 1 X: wont do
// 2 ~: WIP
// total: 51
// ---------------------------------------------------
// (X) 1 : wont do
//
// col 1: 50
// --------------
// 44 f: ffi function imported by bindgen
// F: ffi function wrapped safely
// 6 r: static function reimplemented in Rust
// (f) 44 : unsafe ffi function exported by bindgen
// (w) 0 : safely wrapped ffi function
// (r) 6 : static function manually reimplemented
//
// col 2: 48
// --------------
// 38 m: impl as a `Notcurses` method
// 10 M: impl for the `Nc` wrapper struct too
//
// col 3: 13
// --------------
// 13 t: tests done for the ffi or reimplemented funtion
// T: tests done also for the m method
// Ŧ: tests done also for the M method wrapper struct
// --- -------------------------------------------------------------------------
// (m) 38 : method implemented
// (~) 3 : work in progress
//
// (t) 13 : unit test done for the function
// (T) 0 : unit test done also for the method
// ---------------------------------------------------
// fm notcurses_at_yx
// fm notcurses_bottom
// fm notcurses_canbraille
@ -35,18 +25,18 @@
// fmt notcurses_cantruecolor
// fmt notcurses_canutf8
// fm notcurses_check_pixel_support
// f~ notcurses_core_init
//~f notcurses_core_init
// fm notcurses_cursor_disable
// fm notcurses_cursor_enable
// fmt notcurses_debug
// fm notcurses_debug_caps
// fmt notcurses_drop_planes
// fm notcurses_getc
// fMt notcurses_init
// fmt notcurses_init
// fm notcurses_inputready_fd
// fM notcurses_lex_blitter
// fM notcurses_lex_margins
// fM notcurses_lex_scalemode
// fm notcurses_lex_blitter
// fm notcurses_lex_margins
// fm notcurses_lex_scalemode
// fm notcurses_linesigs_disable
// fm notcurses_linesigs_enable
// fm notcurses_mouse_disable
@ -61,16 +51,15 @@
// fm notcurses_stats_reset
// fm notcurses_stdplane
// fm notcurses_stdplane_const
// fMt notcurses_stop
// fM notcurses_str_blitter
// fM notcurses_str_scalemode
// fmt notcurses_stop
// fm notcurses_str_blitter
// fm notcurses_str_scalemode
// fm notcurses_supported_styles
// fm notcurses_top
//X notcurses_ucs32_to_utf8 (not needed in rust)
// fMt notcurses_version
// fM notcurses_version_components
//
// rMt notcurses_align
// fmt notcurses_version
// fm notcurses_version_components
// rmt notcurses_align
// rm notcurses_getc_blocking
// rm notcurses_getc_nblock
//~r notcurses_stddim_yx // multiple mutable references errors
@ -83,12 +72,10 @@ mod test;
mod helpers;
mod methods;
mod reimplemented;
mod wrapper;
#[allow(unused_imports)]
pub(crate) use helpers::*;
pub use reimplemented::*;
pub use wrapper::*;
/// Notcurses builds atop the terminfo abstraction layer to
/// provide reasonably portable vivid character displays.

View File

@ -1,137 +0,0 @@
//! `Nc` wrapper struct and traits implementations.
use std::ops::{Deref, DerefMut};
use crate::{
raw_wrap, NcAlign, NcBlitter, NcDim, NcLogLevel, NcResult, NcScale, Notcurses, NotcursesOptions,
};
/// Safe wrapper around [Notcurses], the main struct of the TUI library.
#[derive(Debug)]
pub struct Nc<'a> {
pub(crate) raw: &'a mut Notcurses,
}
impl<'a> AsRef<Notcurses> for Nc<'a> {
fn as_ref(&self) -> &Notcurses {
self.raw
}
}
impl<'a> AsMut<Notcurses> for Nc<'a> {
fn as_mut(&mut self) -> &mut Notcurses {
self.raw
}
}
impl<'a> Deref for Nc<'a> {
type Target = Notcurses;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> DerefMut for Nc<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl<'a> Drop for Nc<'a> {
/// Destroys the Nc context.
fn drop(&mut self) {
let _ = self.raw.stop();
}
}
/// # Constructors and methods overriden from Notcurses
impl<'a> Nc<'a> {
// wrap constructors
/// New Nc (without banners).
pub fn new() -> NcResult<Self> {
raw_wrap![Notcurses::new()]
}
/// New Nc, with banners.
pub fn with_banners() -> NcResult<Self> {
raw_wrap![Notcurses::with_banners()]
}
/// New Nc, without an alternate screen (nor banners).
pub fn without_altscreen() -> NcResult<Self> {
raw_wrap![Notcurses::without_altscreen()]
}
/// New Nc, expects `NCOPTION_*` flags.
pub fn with_flags(flags: u64) -> NcResult<Self> {
raw_wrap![Notcurses::with_flags(flags)]
}
/// New Nc, expects [NotcursesOptions].
pub fn with_options(options: NotcursesOptions) -> NcResult<Self> {
raw_wrap![Notcurses::with_options(options)]
}
/// New Nc, expects [NcLogLevel] and flags.
pub fn with_debug(loglevel: NcLogLevel, flags: u64) -> NcResult<Self> {
raw_wrap![Notcurses::with_debug(loglevel, flags)]
}
// disable destructor
/// Since Nc already implements [Drop](#impl-Drop),
/// this function is made no-op.
pub fn stop(&mut self) -> NcResult<()> {
Ok(())
}
// wrap associated functions
/// Returns the offset into `availcols` at which `cols` ought be output given
/// the requirements of `align`.
pub fn align(availcols: NcDim, align: NcAlign, cols: NcDim) -> NcResult<()> {
Notcurses::align(availcols, align, cols)
}
/// Gets the name of an [NcBlitter] blitter.
pub fn str_blitter(blitter: NcBlitter) -> String {
Notcurses::str_blitter(blitter)
}
/// Gets the name of an [NcScale] scaling mode.
pub fn str_scalemode(scalemode: NcScale) -> String {
Notcurses::str_scalemode(scalemode)
}
/// Returns an [NcBlitter] from a string representation.
pub fn lex_blitter(op: &str) -> NcResult<NcBlitter> {
Notcurses::lex_blitter(op)
}
/// Lexes a margin argument according to the standard Notcurses definition.
///
/// There can be either a single number, which will define all margins equally,
/// or there can be four numbers separated by commas.
///
pub fn lex_margins(op: &str, options: &mut NotcursesOptions) -> NcResult<()> {
Notcurses::lex_margins(op, options)
}
/// Returns an [NcScale] from a string representation.
pub fn lex_scalemode(op: &str) -> NcResult<NcScale> {
Notcurses::lex_scalemode(op)
}
/// Returns a human-readable string describing the running Nc version.
pub fn version() -> String {
Notcurses::version()
}
/// Returns the running Notcurses version components
/// (major, minor, patch, tweak).
pub fn version_components() -> (u32, u32, u32, u32) {
Notcurses::version_components()
}
}