1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
//! A terminal-based text editor with goals to maximize simplicity and efficiency. //! //! # Design Goals //! 1) All functionality shall be able to be performed via the keys reachable from the home row. Where it makes sense, functionality may additionally be performed via the mouse and other keys. //! 2) All user input shall be modal, i.e. keys may implement different functionality depending on the current mode of the application. //! 3) Paper shall utilize already implemented tools and commands wherever possible; specifically paper shall support the [Language Server Protocol]. //! //! [Language Server Protocol]: https://microsoft.github.io/language-server-protocol/ #![allow( clippy::unreachable, // unreachable added by enum_map::Enum. clippy::use_self, // False positive on format macro. clippy::trivial_regex, // Trivial regex added by thiserror::Error. )] mod app; mod io; mod logging; mod orient; // Export so that other crates can build Arguments. pub use logging::LogConfig; use { app::Processor, // Avoid use of std Option. core::option::Option, fehler::{throw, throws}, io::{ ConsumeInputError, ConsumeInputIssue, CreateInterfaceError, Interface, ProduceOutputError, }, log::{error, info}, logging::InitLoggerError, market::{Consumer, Producer}, structopt::StructOpt, thiserror::Error as ThisError, }; /// Arguments for [`Paper`] initialization. /// /// [`Paper`]: ../struct.Paper.html #[derive(Clone, Debug, Default, StructOpt)] pub struct Arguments { /// The file to be viewed. #[structopt(value_name("FILE"))] file: Option<String>, #[allow(clippy::missing_docs_in_private_items)] // Flattened structs do not allow doc comments. #[structopt(flatten)] log_config: LogConfig, } /// An instance of the `paper` application. /// /// Once [`Paper`] has started running, it shall continue until it has been terminated. **Termination** occurs when the application is ordered to quit or an unrecoverable error is thrown. /// /// When [`Paper`] is dropped, it shall kill all spawned processes and return the user interface to its previous state. /// /// # Examples /// /// ```no_run /// use paper::{Arguments, Failure, Paper}; /// # fn main() -> Result<(), Failure> { /// /// Paper::new(Arguments::default())?.run()?; /// # Ok(()) /// # } /// ``` #[derive(Debug)] pub struct Paper { /// The interface with external processes. io: Interface, /// The application processor. processor: Processor, } impl Paper { /// Creates a new instance of `paper`. #[inline] #[throws(CreateError)] pub fn new(arguments: Arguments) -> Self { // Logger is created first so all other parts can use it. logging::init(arguments.log_config)?; Self { io: Interface::new(arguments.file)?, processor: Processor::new(), } } /// Runs the application. /// /// This function shall run until `paper` has been **terminated**. /// /// # Errors /// /// If any unrecoverable error is thrown, a [`RunError`] shall be thrown. /// /// [`RunError`]: enum.RunError.html #[inline] #[throws(RunError)] pub fn run(&mut self) { if let Err(error) = self.execute() { error!("{}", error); throw!(error); } info!("Application quitting"); } /// Loops through execution until `paper` has been **terminated**. /// /// # Errors /// /// If any unrecoverable error is thrown, a [`RunError`] shall be thrown. /// /// [`RunError`]: enum.RunError.html #[throws(RunError)] fn execute(&mut self) { loop { match self.io.demand() { Ok(input) => self.io.force_all(self.processor.process(input))?, Err(issue) => { if let ConsumeInputIssue::Error(error) = issue { throw!(error); } break; } } } self.io.join(); } } /// An error from which `paper` is unable to recover. #[derive(Debug, ThisError)] pub enum Failure { /// An error creating `paper`. #[error(transparent)] Create(#[from] CreateError), /// An error running `paper`. #[error(transparent)] Run(#[from] RunError), } /// An error creating a [`Paper`]. /// /// [`Paper`]: struct.Paper.html #[derive(Debug, ThisError)] pub enum CreateError { /// An error creating the application logger. #[error("Failed to initialize logger: {0}")] Logger(#[from] InitLoggerError), /// An error creating the [`Interface`]. /// /// [`Interface`]: io/struct.Interface.html #[error("Failed to create application: {0}")] Interface(#[from] CreateInterfaceError), } /// An error running `paper`. #[derive(Debug, ThisError)] pub enum RunError { /// An error consuming an input. #[error("Failed to consume input: {0}")] Consume(#[from] ConsumeInputError), /// An error producing an output. #[error("Failed to produce output: {0}")] Produce(#[from] ProduceOutputError), }