aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-01-06 11:14:24 -0800
committerRose Hogenson <rosehogenson@posteo.net>2024-01-06 11:14:24 -0800
commitb5f471c6a5c517605aa0c53738d4e916579ede78 (patch)
treee227878c26fb2423aebe89ee7653e7aa859e9f93
parentdbb1ba202bb5803af1b580b588e7aeef0c6f7e31 (diff)
downloadeditor-b5f471c6a5c517605aa0c53738d4e916579ede78.tar.zst
Use more idiomatic error handling.
-rw-r--r--src/main.rs141
-rw-r--r--src/rope.rs64
-rw-r--r--src/term.rs55
3 files changed, 82 insertions, 178 deletions
diff --git a/src/main.rs b/src/main.rs
index 599ed36..a427d27 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@ mod term;
use rope::Rope;
use std::ffi::{OsStr, OsString};
use std::io::{stdout, stdin, Write, Read};
+use std::error::Error;
use std::path::Path;
use term::Key;
@@ -15,77 +16,56 @@ struct State {
line_cols: usize,
}
-impl State {
- fn cursor_pos(&mut self) -> Result<(usize, usize), String> {
- if let Err(err) = write!(stdout(), "\x1b[6n") {
- return Err(format!("cursor position: status report: {}", err));
- }
- if let Err(err) = stdout().flush() {
- return Err(format!("cursor position: status report: {}", err));
+fn parse_status_report(buf: &[u8]) -> Result<(usize, usize), Box<dyn Error>> {
+ let mut semicolon = 0;
+ for (i, &c) in buf.iter().enumerate() {
+ if c == b';' {
+ semicolon = i;
+ break;
}
+ }
+ if semicolon < 2 || semicolon == buf.len()-1 {
+ return Err(Box::from("invalid response"));
+ }
+ let row_str = std::str::from_utf8(&buf[2..semicolon])?;
+ let col_str = std::str::from_utf8(&buf[semicolon+1..buf.len()-1])?;
+ let row = row_str.parse::<usize>()?;
+ let col = col_str.parse::<usize>()?;
+ // Yuck... 1 indexing
+ return Ok((row-1, col-1));
+}
+
+impl State {
+ fn cursor_pos(&mut self) -> Result<(usize, usize), Box<dyn Error>> {
+ write!(stdout(), "\x1b[6n").map_err(|err| format!("cursor position: status report: {}", err))?;
+ stdout().flush().map_err(|err| format!("cursor position: status report: {}", err))?;
let mut buf = vec![0; 20];
- let n = match stdin().read(&mut buf) {
- Ok(n) => n,
- Err(err) => {
- return Err(format!("cursor position: read status report: {}", err));
- }
- };
+ let n = stdin().read(&mut buf).map_err(|err| format!("cursor position: read status report: {}", err))?;
buf.truncate(n);
- let mut semicolon = 0;
- for (i, &c) in buf.iter().enumerate() {
- if c == b';' {
- semicolon = i;
- break;
- }
- }
- let row_str = match std::str::from_utf8(&buf[2..semicolon]) {
- Ok(row_str) => row_str,
- Err(_) => {
- return Err(format!("cursor position: invalid response {}", String::from_utf8_lossy(&buf)));
- }
- };
- let col_str = match std::str::from_utf8(&buf[semicolon+1..buf.len()-1]) {
- Ok(col_str) => col_str,
- Err(_) => {
- return Err(format!("cursor position: invalid response {}", String::from_utf8_lossy(&buf)));
- }
- };
- let row = match row_str.parse::<usize>() {
- Ok(row) => row,
- Err(_) => {
- return Err(format!("cursor position: invalid response {}", String::from_utf8_lossy(&buf)));
- }
- };
- let col = match col_str.parse::<usize>() {
- Ok(col) => col,
- Err(_) => {
- return Err(format!("cursor position: invalid response {}", String::from_utf8_lossy(&buf)));
- }
- };
- // Yuck... 1 indexing
- return Ok((row-1, col-1));
+ let pos = parse_status_report(&buf).map_err(|_| format!("cursor position: invalid response: {}", String::from_utf8_lossy(&buf)))?;
+ return Ok(pos);
}
- fn repaint_line_full(&mut self) {
- let _ = write!(stdout(), "\x1b[{}H", self.cursor_row+1);
+ fn repaint_line_full(&mut self) -> Result<(), Box<dyn Error>> {
+ write!(stdout(), "\x1b[{}H", self.cursor_row+1)?;
let line = self.buf.line(self.cursor_row);
- let _ = line.print(&mut stdout());
- let (_, col) = self.cursor_pos().expect("asdf");
+ line.print(&mut stdout())?;
+ let (_, col) = self.cursor_pos()?;
self.line_cols = col;
if self.cursor_col >= self.line_cols {
self.line_offset = line.len();
- return;
+ return Ok(());
}
- let mut lo = line.char(self.cursor_col).expect("at this point, cursor_col should have some valid offset");
+ let mut lo = line.ceil_char_boundary(self.cursor_col);
let mut hi = line.len();
// Binary search :D
while hi > lo {
let x = line.floor_char_boundary(lo + (hi - lo) / 2);
- let _ = write!(stdout(), "\x1b[G");
- let _ = line.slice(0, x).print(&mut stdout());
- let (_, col) = self.cursor_pos().expect("ouchie, I should really handle this I guess");
+ write!(stdout(), "\x1b[G")?;
+ line.slice(0, x).print(&mut stdout())?;
+ let (_, col) = self.cursor_pos()?;
if col <= self.cursor_col {
lo = line.ceil_char_boundary(x+1);
} else {
@@ -93,32 +73,18 @@ impl State {
}
}
self.line_offset = line.floor_char_boundary(lo-1);
- let _ = write!(stdout(), "\x1b[G\x1b[K");
- let _ = line.print(&mut stdout());
- let _ = write!(stdout(), "\x1b[{}G", self.cursor_col+1);
+ write!(stdout(), "\x1b[G\x1b[K")?;
+ line.print(&mut stdout())?;
+ write!(stdout(), "\x1b[{}G", self.cursor_col+1)?;
+ return Ok(());
}
}
-fn edit(file: &OsStr) -> Result<(), String> {
- let _raw_handle = match term::raw() {
- Some(h) => h,
- None => {
- return Err(String::from("cannot put terminal in raw mode"));
- }
- };
+fn edit(file: &OsStr) -> Result<(), Box<dyn Error>> {
+ let _raw_handle = term::raw().map_err(|err| format!("cannot put terminal in raw mode: {}", err))?;
- let r = match Rope::open(Path::new(file)) {
- Ok(r) => r,
- Err(err) => {
- return Err(format!("open file {}: {}", file.to_string_lossy(), err));
- }
- };
- let size = match term::size() {
- Some(size) => size,
- None => {
- return Err(String::from("cannot get terminal size"));
- }
- };
+ let r = Rope::open(Path::new(file))?;
+ let size = term::size()?;
let mut state = State{
buf: r,
cursor_row: 0,
@@ -146,20 +112,13 @@ fn edit(file: &OsStr) -> Result<(), String> {
let _ = stdout().flush();
loop {
- let c = match term::read_key(&mut stdin()) {
- Ok(c) => c,
- Err(err) => {
- return Err(err);
- }
- };
+ let c = term::read_key(&mut stdin()).map_err(|err| format!("read key: {}", err))?;
match c {
Key::CtrlQ => {
break;
}
Key::CtrlS => {
- if let Err(err) = state.buf.save(Path::new(file)) {
- return Err(format!("write file {}: {}", file.to_string_lossy(), err));
- }
+ let _ = state.buf.save(Path::new(file));
}
Key::Up => {
if state.cursor_row == 0 {
@@ -167,7 +126,7 @@ fn edit(file: &OsStr) -> Result<(), String> {
}
state.cursor_row -= 1;
- state.repaint_line_full();
+ let _ = state.repaint_line_full();
let _ = stdout().flush();
}
Key::Down => {
@@ -176,7 +135,7 @@ fn edit(file: &OsStr) -> Result<(), String> {
}
state.cursor_row += 1;
- state.repaint_line_full();
+ let _ = state.repaint_line_full();
let _ = stdout().flush();
}
Key::Left => {
@@ -185,7 +144,7 @@ fn edit(file: &OsStr) -> Result<(), String> {
continue;
}
state.cursor_col -= 1;
- state.repaint_line_full();
+ let _ = state.repaint_line_full();
let _ = stdout().flush();
}
Key::Right => {
@@ -193,14 +152,14 @@ fn edit(file: &OsStr) -> Result<(), String> {
continue;
}
state.cursor_col += 1;
- state.repaint_line_full();
+ let _ = state.repaint_line_full();
let _ = stdout().flush();
}
Key::Char(c) => {
state.cursor_col = std::cmp::min(state.cursor_col, state.line_cols);
state.buf = state.buf.insert(state.buf.line_idx(state.cursor_row)+state.line_offset, c);
state.cursor_col += 1;
- state.repaint_line_full();
+ let _ = state.repaint_line_full();
let _ = stdout().flush();
}
}
diff --git a/src/rope.rs b/src/rope.rs
index ea37e6c..c5e9aa1 100644
--- a/src/rope.rs
+++ b/src/rope.rs
@@ -1,6 +1,7 @@
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::path::Path;
+use std::error::Error;
use std::rc::Rc;
// Set to a small value to help identify bugs in the implementation.
@@ -167,13 +168,8 @@ impl Rope {
}
}
- pub fn open(path: &Path) -> Result<Rope, String> {
- let mut f = match File::open(path) {
- Ok(f) => f,
- Err(err) => {
- return Err(format!("open file {}: {}", path.display(), err));
- }
- };
+ pub fn open(path: &Path) -> Result<Rope, Box<dyn Error>> {
+ let mut f = File::open(path).map_err(|err| format!("open file {}: {}", path.display(), err))?;
let mut rope = Rope::leaf(Vec::new());
loop {
let mut buf = vec![0; MAX_NODE_SIZE];
@@ -183,7 +179,7 @@ impl Rope {
if err.kind() == ErrorKind::Interrupted {
continue;
}
- return Err(format!("read file {}: {}", path.display(), err));
+ return Err(Box::from(format!("read file {}: {}", path.display(), err)));
}
};
if n == 0 {
@@ -196,19 +192,10 @@ impl Rope {
return Ok(rope);
}
- pub fn save(&self, path: &Path) -> Result<(), String> {
- let mut f = match File::create(path) {
- Ok(f) => f,
- Err(err) => {
- return Err(format!("save: create file {}: {}", path.display(), err));
- }
- };
- if let Err(err) = self.print(&mut f) {
- return Err(format!("save: write file {}: {}", path.display(), err));
- }
- if let Err(err) = f.sync_all() {
- return Err(format!("save: write file {}: {}", path.display(), err));
- }
+ pub fn save(&self, path: &Path) -> Result<(), Box<dyn Error>> {
+ let mut f = File::create(path).map_err(|err| format!("save: create file {}: {}", path.display(), err))?;
+ self.print(&mut f).map_err(|err| format!("save: write file {}: {}", path.display(), err))?;
+ f.sync_all().map_err(|err| format!("save: write file {}: {}", path.display(), err))?;
return Ok(());
}
@@ -260,41 +247,6 @@ impl Rope {
return self.slice(start, end-1);
}
- pub fn char(&self, n: usize) -> Result<usize, usize> {
- match self {
- Rope(Node::Leaf(l)) => {
- let mut count = 0;
- for (i, &b) in l.bytes().iter().enumerate() {
- if b & 0xc0 == 0x80 {
- continue;
- }
- if count == n {
- return Ok(i);
- }
- count += 1;
- }
- return Err(count);
- }
- Rope(Node::Branch(b)) => {
- match b.left.char(n) {
- Ok(i) => {
- return Ok(i);
- }
- Err(len) => {
- match b.right.char(n - len) {
- Ok(i) => {
- return Ok(b.left.len() + i);
- }
- Err(right_len) => {
- return Err(len + right_len);
- }
- }
- }
- }
- }
- }
- }
-
fn is_char_boundary(&self, index: usize) -> bool {
match self {
Rope(Node::Leaf(l)) => {
diff --git a/src/term.rs b/src/term.rs
index c4319b4..c23dc4e 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -1,32 +1,36 @@
use libc::{termios, winsize};
+use std::error::Error;
use std::io::{Read, Stdin};
-pub fn size() -> Option<winsize> {
+pub fn size() -> Result<winsize, Box<dyn Error>> {
unsafe {
let mut window: winsize = std::mem::zeroed();
- if libc::ioctl(1, libc::TIOCGWINSZ, &mut window) < 0 {
- return None;
+ let status = libc::ioctl(1, libc::TIOCGWINSZ, &mut window);
+ if status < 0 {
+ return Err(Box::from(format!("terminal size: ioctl failed with code {}", status)));
}
- return Some(window);
+ return Ok(window);
}
}
-fn get_attr() -> Option<termios> {
+fn get_attr() -> Result<termios, Box<dyn Error>> {
unsafe {
let mut attr: termios = std::mem::zeroed();
- if libc::tcgetattr(1, &mut attr) < 0 {
- return None;
+ let status = libc::tcgetattr(1, &mut attr);
+ if status < 0 {
+ return Err(Box::from(format!("tcgetattr failed with code {}", status)));
}
- return Some(attr);
+ return Ok(attr);
}
}
-fn set_attr(attrs: &termios) -> Option<()> {
+fn set_attr(attrs: &termios) -> Result<(), Box<dyn Error>> {
unsafe {
- if libc::tcsetattr(1, libc::TCSANOW, attrs) < 0 {
- return None;
+ let status = libc::tcsetattr(1, libc::TCSANOW, attrs);
+ if status < 0 {
+ return Err(Box::from(format!("set attributes: code {}", status)));
}
- return Some(());
+ return Ok(());
}
}
@@ -44,21 +48,14 @@ pub struct RawHandle {
impl Drop for RawHandle {
fn drop(&mut self) {
- set_attr(&self.old);
+ let _ = set_attr(&self.old);
}
}
-pub fn raw() -> Option<RawHandle> {
- let old = match get_attr() {
- Some(old) => old,
- None => {
- return None;
- }
- };
- if let None = set_attr(&make_raw()) {
- return None;
- }
- return Some(RawHandle { old: old });
+pub fn raw() -> Result<RawHandle, Box<dyn Error>> {
+ let old = get_attr()?;
+ set_attr(&make_raw())?;
+ return Ok(RawHandle { old: old });
}
pub enum Key {
@@ -71,16 +68,14 @@ pub enum Key {
Char(u8),
}
-pub fn read_key(stdin: &mut Stdin) -> Result<Key, String> {
+pub fn read_key(stdin: &mut Stdin) -> Result<Key, Box<dyn Error>> {
const CTRL_Q: u8 = 17;
const CTRL_S: u8 = 19;
const ESC: u8 = 27;
loop {
let mut buf = vec![0; 1];
- if let Err(err) = stdin.read_exact(&mut buf) {
- return Err(format!("read input: {}", err));
- }
+ stdin.read_exact(&mut buf)?;
if buf[0] == CTRL_Q {
return Ok(Key::CtrlQ);
}
@@ -92,9 +87,7 @@ pub fn read_key(stdin: &mut Stdin) -> Result<Key, String> {
}
// Try to handle an escape sequence.
let mut buf = vec![0; 2];
- if let Err(err) = stdin.read_exact(&mut buf) {
- return Err(format!("read input: {}", err));
- }
+ stdin.read_exact(&mut buf)?;
if buf[0] != b'[' {
// Unknown escape sequence, just read another key.
continue;