breed/src/main.rs

133 lines
3.8 KiB
Rust
Raw Permalink Normal View History

2023-04-11 21:20:12 +00:00
/*
2023-04-11 20:02:34 +00:00
* Copyright (c) 2023 Marceline Cramer
2023-04-11 22:12:56 +00:00
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
2023-04-11 20:02:34 +00:00
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
2023-04-11 21:20:12 +00:00
* the terms of the GNU Affero General Public License as published by the Free
2023-04-11 20:02:34 +00:00
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
2023-04-11 21:20:12 +00:00
*
2023-04-11 20:02:34 +00:00
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
2023-04-11 22:12:56 +00:00
use std::{
env::args,
2023-04-12 05:22:16 +00:00
ffi::OsString,
2023-04-18 23:28:59 +00:00
fs::File,
io::{Read, Stdout, Write},
2023-04-11 22:26:02 +00:00
os::fd::FromRawFd,
2023-04-18 23:28:59 +00:00
path::Path,
2023-04-11 22:12:56 +00:00
};
2023-04-11 19:56:43 +00:00
2023-04-18 23:28:59 +00:00
use crossterm::{terminal, QueueableCommand, Result};
2023-04-12 03:55:33 +00:00
use yacexits::{exit, EX_DATAERR, EX_UNAVAILABLE};
2023-04-11 19:56:43 +00:00
2023-04-12 19:36:38 +00:00
mod actions;
2023-04-12 03:55:56 +00:00
mod buffer;
2023-04-12 21:13:20 +00:00
mod config;
2023-04-13 04:04:21 +00:00
mod keybinds;
2023-04-18 23:28:59 +00:00
mod state;
2023-04-12 03:34:24 +00:00
mod theme;
2023-04-11 19:56:43 +00:00
#[derive(Copy, Clone, Debug, Default)]
pub struct Cursor {
2023-04-11 19:56:43 +00:00
pub column: usize,
pub line: usize,
}
#[derive(Copy, Clone, Debug)]
pub enum Direction {
2023-04-11 19:56:43 +00:00
Left,
Down,
Up,
Right,
}
2023-04-18 23:28:59 +00:00
fn screen_main(stdout: &mut Stdout, mut state: state::State) -> Result<()> {
2023-04-12 21:13:20 +00:00
let poll_timeout = std::time::Duration::from_millis(100);
2023-04-11 19:56:43 +00:00
while !state.quit {
2023-04-11 20:04:29 +00:00
state.draw(stdout)?;
2023-04-12 21:13:20 +00:00
if let true = crossterm::event::poll(poll_timeout)? {
let event = crossterm::event::read()?;
state.on_event(event);
}
2023-04-11 19:56:43 +00:00
}
Ok(())
2023-04-11 17:40:10 +00:00
}
2023-04-11 20:04:29 +00:00
fn main() -> Result<()> {
2023-04-11 22:12:56 +00:00
let argv = args().collect::<Vec<String>>();
2023-04-11 22:26:02 +00:00
let mut buf = Vec::new();
2023-04-12 05:22:16 +00:00
let file_name: Option<OsString>;
2023-04-11 22:12:56 +00:00
2023-04-11 22:49:11 +00:00
match argv.get(1).map(|s| s.as_str()) {
Some("-") | None => {
2023-04-12 05:22:16 +00:00
file_name = None;
let stdin = 0; // get stdin as a file descriptor
if unsafe { libc::isatty(stdin) } == 0 {
unsafe { File::from_raw_fd(stdin) }
2023-04-12 21:16:07 +00:00
} else {
2023-04-14 18:53:55 +00:00
File::open("/dev/null")?
2023-04-12 21:16:07 +00:00
}
}
2023-04-11 22:26:02 +00:00
Some(path) => {
2023-04-12 05:22:16 +00:00
let input_file = Path::new(path);
2023-04-14 18:53:55 +00:00
file_name = Some(OsString::from(&path));
2023-04-12 05:22:16 +00:00
File::open(input_file).unwrap_or_else(|_| {
let mut err = String::new();
if !input_file.exists() {
err = "No such file or directory.".to_string();
} else if input_file.is_dir() {
err = "Is a directory.".to_string();
}
eprintln!("{}: {}: {}", argv[0], path, err);
2023-04-11 22:49:11 +00:00
exit(EX_UNAVAILABLE);
2023-04-11 22:26:02 +00:00
})
2023-04-12 21:16:07 +00:00
}
}
2023-04-14 18:53:55 +00:00
.read_to_end(&mut buf)?;
2023-04-11 22:49:11 +00:00
let text = String::from_utf8(buf).unwrap_or_else(|_| {
eprintln!(
2023-04-12 03:55:33 +00:00
"{}: {}: File contents are not valid UTF-8.",
argv[0], argv[1]
2023-04-11 22:49:11 +00:00
);
exit(EX_DATAERR);
});
2023-04-11 22:12:56 +00:00
2023-04-18 23:28:59 +00:00
let state = state::State::from_str(file_name, &text)?;
// begin to enter alternate screen
let mut stdout = std::io::stdout();
2023-04-11 20:04:29 +00:00
terminal::enable_raw_mode()?;
2023-04-12 21:50:14 +00:00
stdout.queue(terminal::EnterAlternateScreen)?;
// register a panic handler to exit the alternate screen
let old_panic_handler = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let mut stdout = std::io::stdout();
let _ = stdout.queue(terminal::LeaveAlternateScreen);
let _ = terminal::disable_raw_mode();
let _ = stdout.flush();
old_panic_handler(info)
}));
// run inner event loop
2023-04-11 20:04:29 +00:00
let result = screen_main(&mut stdout, state);
// exit alternate screen
2023-04-12 21:50:14 +00:00
stdout.queue(terminal::LeaveAlternateScreen)?;
2023-04-11 20:04:29 +00:00
terminal::disable_raw_mode()?;
2023-04-11 20:04:29 +00:00
result
}