xrl/lib.rs
1//! `xrl` is a Tokio based library to build clients for the Xi editor. The
2//! challenge with Xi RPC is that endpoints are both client (sending
3//! requests/notifications) and server (handling incoming
4//! requests/notifications).
5//!
6//!
7//! ```rust
8//! extern crate futures;
9//! extern crate tokio;
10//! extern crate xrl;
11//!
12//! use futures::{future, Future, Stream};
13//! use xrl::*;
14//!
15//! // Type that represent a `xi-core` peer. It implements `Frontend`,
16//! // which means it can handle notifications and requests from
17//! // `xi-core`.
18//! #[allow(dead_code)]
19//! struct MyFrontend {
20//! // This is not actually used in this example, but if we wanted to
21//! // our frontend could use a `Client` so that it could send
22//! // requests and notifications to `xi-core`, instead of just
23//! // handling incoming messages.
24//! client: Client,
25//! }
26//!
27//! // Implement how our client handles notifications & requests from the core.
28//! impl Frontend for MyFrontend {
29//! type NotificationResult = Result<(), ()>;
30//! fn handle_notification(&mut self, notification: XiNotification) -> Self::NotificationResult {
31//! use XiNotification::*;
32//! match notification {
33//! Update(update) => println!("received `update` from Xi core:\n{:?}", update),
34//! ScrollTo(scroll) => println!("received `scroll_to` from Xi core:\n{:?}", scroll),
35//! DefStyle(style) => println!("received `def_style` from Xi core:\n{:?}", style),
36//! AvailablePlugins(plugins) => {
37//! println!("received `available_plugins` from Xi core:\n{:?}", plugins)
38//! }
39//! UpdateCmds(cmds) => println!("received `update_cmds` from Xi core:\n{:?}", cmds),
40//! PluginStarted(plugin) => {
41//! println!("received `plugin_started` from Xi core:\n{:?}", plugin)
42//! }
43//! PluginStoped(plugin) => {
44//! println!("received `plugin_stoped` from Xi core:\n{:?}", plugin)
45//! }
46//! ConfigChanged(config) => {
47//! println!("received `config_changed` from Xi core:\n{:?}", config)
48//! }
49//! ThemeChanged(theme) => println!("received `theme_changed` from Xi core:\n{:?}", theme),
50//! Alert(alert) => println!("received `alert` from Xi core:\n{:?}", alert),
51//! AvailableThemes(themes) => {
52//! println!("received `available_themes` from Xi core:\n{:?}", themes)
53//! }
54//! FindStatus(status) => println!("received `find_status` from Xi core:\n{:?}", status),
55//! ReplaceStatus(status) => {
56//! println!("received `replace_status` from Xi core:\n{:?}", status)
57//! }
58//! AvailableLanguages(langs) => {
59//! println!("received `available_languages` from Xi core:\n{:?}", langs)
60//! }
61//! LanguageChanged(lang) => {
62//! println!("received `language_changed` from Xi core:\n{:?}", lang)
63//! }
64//! }
65//! Ok(())
66//! }
67//!
68//! type MeasureWidthResult = Result<Vec<Vec<f32>>, ()>;
69//! // we don't actually use the `request` argument in this example,
70//! // hence the attribute.
71//! #[allow(unused_variables)]
72//! fn handle_measure_width(&mut self, request: MeasureWidth) -> Self::MeasureWidthResult {
73//! Ok(Vec::new())
74//! }
75//! }
76//!
77//! struct MyFrontendBuilder;
78//!
79//! impl FrontendBuilder for MyFrontendBuilder {
80//! type Frontend = MyFrontend;
81//! fn build(self, client: Client) -> Self::Frontend {
82//! MyFrontend { client }
83//! }
84//! }
85//!
86//! fn init_xrl() {
87//! tokio::run(future::lazy(move || {
88//! // spawn Xi core
89//! let (client, core_stderr) = spawn("xi-core", MyFrontendBuilder {}).unwrap();
90//!
91//! // start logging Xi core's stderr
92//! tokio::spawn(
93//! core_stderr
94//! .for_each(|msg| {
95//! println!("xi-core stderr: {}", msg);
96//! Ok(())
97//! })
98//! .map_err(|_| ()),
99//! );
100//!
101//! let client_clone = client.clone();
102//! client
103//! // Xi core expects the first notification to be
104//! // "client_started"
105//! .client_started(None, None)
106//! .map_err(|e| eprintln!("failed to send \"client_started\": {:?}", e))
107//! .and_then(move |_| {
108//! let client = client_clone.clone();
109//! client
110//! .new_view(None)
111//! .map(|view_name| println!("opened new view: {}", view_name))
112//! .map_err(|e| eprintln!("failed to open a new view: {:?}", e))
113//! .and_then(move |_| {
114//! // Forces to shut down the Xi-RPC
115//! // endoint. Otherwise, this example would keep
116//! // running until the xi-core process
117//! // terminates.
118//! println!("shutting down");
119//! client_clone.shutdown();
120//! Ok(())
121//! })
122//! })
123//! }));
124//! }
125//! ```
126
127#![deny(clippy::all)]
128#![allow(clippy::type_complexity)]
129
130#[macro_use]
131extern crate log;
132#[macro_use]
133extern crate serde_derive;
134#[macro_use]
135extern crate serde_json;
136
137mod cache;
138mod client;
139mod core;
140mod errors;
141mod frontend;
142mod protocol;
143mod structs;
144
145pub use crate::cache::LineCache;
146pub use crate::client::Client;
147pub use crate::core::{spawn, spawn_command, CoreStderr};
148pub use crate::errors::{ClientError, ServerError};
149pub use crate::frontend::{Frontend, FrontendBuilder, XiNotification};
150pub use crate::protocol::IntoStaticFuture;
151pub use crate::structs::{
152 Alert, AvailableLanguages, AvailablePlugins, AvailableThemes, ConfigChanged, ConfigChanges,
153 FindStatus, LanguageChanged, Line, MeasureWidth, ModifySelection, Operation, OperationType,
154 PluginStarted, PluginStoped, Position, Query, ReplaceStatus, ScrollTo, Status, Style, StyleDef,
155 ThemeChanged, ThemeSettings, Update, UpdateCmds, ViewId,
156};