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
//! `xrl` is a Tokio based library to build clients for the Xi editor. The //! challenge with Xi RPC is that endpoints are both client (sending //! requests/notifications) and server (handling incoming //! requests/notifications). //! //! //! ```rust //! extern crate futures; //! extern crate tokio; //! extern crate xrl; //! //! use futures::{future, Future, Stream}; //! use xrl::*; //! //! // Type that represent a `xi-core` peer. It implements `Frontend`, //! // which means it can handle notifications and requests from //! // `xi-core`. //! #[allow(dead_code)] //! struct MyFrontend { //! // This is not actually used in this example, but if we wanted to //! // our frontend could use a `Client` so that it could send //! // requests and notifications to `xi-core`, instead of just //! // handling incoming messages. //! client: Client, //! } //! //! // Implement how our client handles notifications & requests from the core. //! impl Frontend for MyFrontend { //! type NotificationResult = Result<(), ()>; //! fn handle_notification(&mut self, notification: XiNotification) -> Self::NotificationResult { //! use XiNotification::*; //! match notification { //! Update(update) => println!("received `update` from Xi core:\n{:?}", update), //! ScrollTo(scroll) => println!("received `scroll_to` from Xi core:\n{:?}", scroll), //! DefStyle(style) => println!("received `def_style` from Xi core:\n{:?}", style), //! AvailablePlugins(plugins) => { //! println!("received `available_plugins` from Xi core:\n{:?}", plugins) //! } //! UpdateCmds(cmds) => println!("received `update_cmds` from Xi core:\n{:?}", cmds), //! PluginStarted(plugin) => { //! println!("received `plugin_started` from Xi core:\n{:?}", plugin) //! } //! PluginStoped(plugin) => { //! println!("received `plugin_stoped` from Xi core:\n{:?}", plugin) //! } //! ConfigChanged(config) => { //! println!("received `config_changed` from Xi core:\n{:?}", config) //! } //! ThemeChanged(theme) => println!("received `theme_changed` from Xi core:\n{:?}", theme), //! Alert(alert) => println!("received `alert` from Xi core:\n{:?}", alert), //! AvailableThemes(themes) => { //! println!("received `available_themes` from Xi core:\n{:?}", themes) //! } //! FindStatus(status) => println!("received `find_status` from Xi core:\n{:?}", status), //! ReplaceStatus(status) => { //! println!("received `replace_status` from Xi core:\n{:?}", status) //! } //! AvailableLanguages(langs) => { //! println!("received `available_languages` from Xi core:\n{:?}", langs) //! } //! LanguageChanged(lang) => { //! println!("received `language_changed` from Xi core:\n{:?}", lang) //! } //! } //! Ok(()) //! } //! //! type MeasureWidthResult = Result<Vec<Vec<f32>>, ()>; //! // we don't actually use the `request` argument in this example, //! // hence the attribute. //! #[allow(unused_variables)] //! fn handle_measure_width(&mut self, request: MeasureWidth) -> Self::MeasureWidthResult { //! Ok(Vec::new()) //! } //! } //! //! struct MyFrontendBuilder; //! //! impl FrontendBuilder for MyFrontendBuilder { //! type Frontend = MyFrontend; //! fn build(self, client: Client) -> Self::Frontend { //! MyFrontend { client } //! } //! } //! //! fn main() { //! tokio::run(future::lazy(move || { //! // spawn Xi core //! let (client, core_stderr) = spawn("xi-core", MyFrontendBuilder {}).unwrap(); //! //! // start logging Xi core's stderr //! tokio::spawn( //! core_stderr //! .for_each(|msg| { //! println!("xi-core stderr: {}", msg); //! Ok(()) //! }) //! .map_err(|_| ()), //! ); //! //! let client_clone = client.clone(); //! client //! // Xi core expects the first notification to be //! // "client_started" //! .client_started(None, None) //! .map_err(|e| eprintln!("failed to send \"client_started\": {:?}", e)) //! .and_then(move |_| { //! let client = client_clone.clone(); //! client //! .new_view(None) //! .map(|view_name| println!("opened new view: {}", view_name)) //! .map_err(|e| eprintln!("failed to open a new view: {:?}", e)) //! .and_then(move |_| { //! // Forces to shut down the Xi-RPC //! // endoint. Otherwise, this example would keep //! // running until the xi-core process //! // terminates. //! println!("shutting down"); //! client_clone.shutdown(); //! Ok(()) //! }) //! }) //! })); //! } //! ``` #![deny(clippy::all)] #![allow(clippy::type_complexity)] #[macro_use] extern crate log; #[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_json; mod cache; mod client; mod core; mod errors; mod frontend; mod protocol; mod structs; pub use crate::cache::LineCache; pub use crate::client::Client; pub use crate::core::{spawn, CoreStderr}; pub use crate::errors::{ClientError, ServerError}; pub use crate::frontend::{Frontend, FrontendBuilder, XiNotification}; pub use crate::protocol::IntoStaticFuture; pub use crate::structs::{ Alert, AvailableLanguages, AvailablePlugins, AvailableThemes, ConfigChanged, ConfigChanges, FindStatus, LanguageChanged, Line, MeasureWidth, ModifySelection, Operation, OperationType, PluginStarted, PluginStoped, Position, Query, ReplaceStatus, ScrollTo, Status, Style, StyleDef, ThemeChanged, ThemeSettings, Update, UpdateCmds, ViewId, };