# sued as a library > [!NOTE] > This page is about sued as a text editing library. For sued as a text editor, > see [README.md](README.md). While sued's primary function is to serve as a text editor with a REPL-style interface - in fact, originally that was its *only* function - since sued v0.18.0, sued's text editing components have now become a library, and the text editor part with the REPL has been split off into `main.rs`. You might want to use sued as a library if you're writing a Rust app that works with plain text and need some way to run text editing commands in an easy and structured way, so you plan on using sued's command parsing and text manipulation functions without the overhead of the entire text editor. Or if you're me and [writing a competitor to VS Code using Tauri and CodeMirror, with sued as a backend](https://codeberg.org/AeriaVelocity/astrion). That last part is exactly half true. I'm nowhere near as huge as Microsoft. Keep in mind, since sued was originally (and still is) intended primarily to be used on its own as a text editor, some faults and/or deficiencies might come about when using sued as a library. While that shouldn't happen, no software is perfect, and sued certainly isn't. ## Usage ### Installing sued as a library #### From crates.io ```bash cargo add sued ``` Cargo will automatically build and manage sued as a library for you. You can update your version of sued as a library by running `cargo update sued`. #### With sued in your project root In your Rust project root, clone and build sued as a library: ```bash git clone https://codeberg.org/AeriaVelocity/sued.git cd sued cargo build --release ``` The above `cargo` command will only pull in `core` features. You may opt for `most` instead: ```bash cargo build --release --features=most ``` Then, add sued as a dependency in your `Cargo.toml`: ```toml # ... [dependencies] sued = { path = "./sued" } # ... ``` And build your project: ```bash cargo build --release ``` You can update your version of sued as a library by running `git pull` in the cloned sued repository. #### As a static Rust library Clone and build sued as a library: ```bash git clone https://codeberg.org/AeriaVelocity/sued.git cd sued cargo build --release ``` Copy `libsued.rlib` to somewhere in your project structure: ```bash mkdir -p path/to/your/project/libs cp ./target/release/libsued.rlib path/to/your/project/libs/libsued.rlib ``` Then, create a `build.rs` file in your project root: ```rust fn main() { println!("cargo:rustc-link-search={}/libs", env!("CARGO_MANIFEST_DIR")); println!("cargo:rustc-link-lib=sued"); // you don't need to specify `libsued.rlib` } ``` And use `extern crate` in your Rust code: ```rust extern crate sued; use sued::{ /* ... */ }; fn main() { // ... } ``` Compiling as a static library and dropping it in your program every time is not recommended for everyday code, and is really only here for super specific use cases. Updating is difficult, since you'd have to run `git pull`, `cargo build --release` and copy `libsued.rlib` to your `libs` folder all over again. It's a lot easier to just [use Cargo](#from-cratesio) to install and manage it. For example, if you're using embedded Rust and actually wanted to legitimately run sued on an Xbox. (This joke will never get old.) ### Using sued in your program > [!NOTE] > The source code of the Cake Shop program described in this section is stored > in [cake-shop-demo](https://codeberg.org/AeriaVelocity/sued/src/branch/main/cake-shop-demo). Let's say, for the sake of argument, you're making a silly little program about cake. There are *two main ways* to utilise sued as a library - the low-level way through **direct functions** via the `suedfn` module, and the high-level way via **processing sued commands** through the editor's own `run_sued_command` function. Before we delve into those, let's create this preamble for our Cake Shop program: ```rust use sued::{EditorState, command::CommandRegistry, file_buffer::FileBuffer}; use sued::{run_sued_command, vecify_command}; fn main() { let cakes = vec![ "chocolate", "vanilla", "strawberry" ]; let mut state = EditorState::instantiate(); state.buffer.contents = cakes.iter().map(|s| s.to_string()).collect(); direct_commands(&mut state); parsed_commands(&mut state); } ``` #### Manually processing sued commands (`fn direct_commands()`) sued commands expect to be provided with an `EditorState`. This isn't hard to create at all, just call `EditorState::instantiate()` to get an `EditorState` with an empty buffer and default commands. However, if you want to call a command, it's not as simple as running `command.call()` - since the command is locked behind an `Arc` (Atomic Reference Counter) and a `Mutex` (mutually exclusive access), you can't just simply *call a command directly*. Instead, you must manually pattern match the existence of the command against your command registry, and if it exists, unwrap the command and call its action. ```rust fn direct_commands(state: &mut EditorState) { let mut cake = String::new(); println!("Welcome to the Direct Cake Shop!"); loop { println!("Please choose a cake from our list:"); if let Some(command) = CommandRegistry::get_command(&mut state.registry, "show") { let mut action = command.action.clone(); // The first argument in `args` is expected to be the name of the // command, and it's ignored by the `show` action. println!("{}", action.call(vec!["show"], state)); } std::io::stdin().read_line(&mut cake).unwrap(); cake = cake.trim().to_lowercase(); match cake.as_str() { "chocolate" | "vanilla" | "strawberry" => { break; } "exit" | "cancel" => { println!("Order cancelled. Thanks for visiting the Cake Shop!"); return; } _ => { if let Ok(id) = cake.parse::() { if id > 0 && id <= state.buffer.contents.len() { cake = state.buffer.contents[id - 1].to_string(); break; } else { println!("The cake ID {} is not in our system. Please try again.", id); } } else { println!("We don't stock {} cakes. Please try again.", cake); } cake.clear(); } } } println!("You ordered a {} cake.", cake); let receipt: Vec = vec![ "== Order ==".to_string(), format!("Cake: {}", cake).to_string(), "== Total ==".to_string(), "£19.99".to_string() ]; receipt.iter().for_each(|s| println!("{}", s)); let filename = "receipt-direct.txt"; // Define a new state to save the receipt, so as not to conflict with // the state defined in `main`, because we need to reuse that state for // `parsed_commands`. let mut out_state = EditorState { buffer: FileBuffer { contents: receipt, file_path: Some(filename.to_string()) }, registry: CommandRegistry::instantiate(), }; if let Some(command) = CommandRegistry::get_command(&mut state.registry, "save") { let mut action = command.action.clone(); action.call(vec!["save", filename], &mut out_state); } println!("Thanks for visiting the Direct Cake Shop!"); } ``` That works, but you may as well be manipulating the text directly. It's useful, sure, but it's also suuuuuper cumbersome - you have to manually unwrap that command and call its action yourself. And, sure, you can define a function to make that more DRY, but it's still Not Great(TM). You really might as well be using [regex](https://github.com/rust-lang/regex) or [sd](https://github.com/chmln/sd) or... really just anything that's on the [Text processing](https://lib.rs/text-processing) list on the lib.rs website. #### Parsing sued commands (`fn parsed_commands()`) Now we move onto the real meat and potatoes - processing sued's commands through the editor's very own `run_sued_command` function! This allows you to enter sued commands as `Vec<&str>` lines, which will then be parsed and processed by the editor. But you probably don't want to write `Vec>`s every time you just want to represent some commands, so there's an easy way to use a simple `Vec<&str>` instead - through the `vecify_command` function. This will split a `&str` into a `Vec<&str>` for you, so you can iterate over a `Vec<&str>` and pass each command through `run_sued_command`. Here's how you can use `run_sued_command` and `vecify_command` to do things the sued way: ```rust fn parsed_commands(state: &mut EditorState) { let mut cake = String::new(); println!("Welcome to the Parsed Cake Shop!"); loop { println!("Please choose a cake from our list:"); // Now, we're ready to do things the sued way! println!("{}", run_sued_command(vecify_command("show"), state)); // See how much more concise that is compared to the `if let Some` code // from `direct_commands`? This is also more idiomatic, since we only // need to pull in `EditorState`, `run_sued_command`, and `vecify_command`, // as opposed to pulling in `CommandRegistry` to look up the command // ourselves. std::io::stdin().read_line(&mut cake).unwrap(); cake = cake.trim().to_lowercase(); match cake.as_str() { "chocolate" | "vanilla" | "strawberry" => { break; } "exit" | "cancel" => { println!("Order cancelled. Thanks for visiting the Cake Shop!"); return; } _ => { if let Ok(id) = cake.parse::() { if id > 0 && id <= state.buffer.contents.len() { cake = state.buffer.contents[id - 1].to_string(); break; } else { println!("The cake ID {} is not in our system. Please try again.", id); } } else { println!("We don't stock {} cakes. Please try again.", cake); } cake.clear(); } } } println!("You ordered a {} cake.", cake); let receipt: Vec = vec![ "== Order ==".to_string(), format!("Cake: {}", cake).to_string(), "== Total ==".to_string(), "£19.99".to_string() ]; receipt.iter().for_each(|s| println!("{}", s)); let filename = "receipt-parsed.txt"; let mut out_state = EditorState { buffer: FileBuffer { contents: receipt, file_path: Some(filename.to_string()) }, registry: CommandRegistry::instantiate(), }; run_sued_command(vecify_command(format!("save {}", filename).as_str()), &mut out_state); println!("Thanks for visiting the Parsed Cake Shop!"); } ``` This code is much more in-line with why `sued` is even a library in the first place - **to provide the functions and features of the [sued text editor](README.md) into other text editors or other programs that work with text.** This is just a simple example, but you can do so much more with this - wherever you edit text, you can probably integrate sued somewhere in there. ## Why? I started a new text editor project, [Astrion](https://codeberg.org/AeriaVelocity/astrion), and I wanted to see if I could integrate sued into it. Since sued's code is very lightweight, it wasn't hard to just refactor its text editing capabilities into a library that I could use in other projects. So, here you go! sued as a library. Thanks and have fun. ## Legal sued (including sued as a library) is distributed under the Apache License, Version 2.0. See [the Legal section of the main README.md](README.md#legal) for the complete rundown.