From be4c770a7c65bb1eb3d8b96128abfc5fa89d63d1 Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Sun, 14 Nov 2021 14:38:57 -0300 Subject: Add Suggestion --- src/lib.rs | 14 +++++++++ src/suggestion.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/suggestion.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 src/suggestion.rs create mode 100644 tests/suggestion.rs diff --git a/src/lib.rs b/src/lib.rs index 2900669..d8c7582 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,18 @@ +// Copyright (c) 2021 Soni L. + +//! Iosonism is a command parsing library. It parses commands from strings, in +//! contrast with an argument parsing library, which parses arrays of strings. +//! +//! Iosonism is based on [Brigadier](https://github.com/Mojang/brigadier). + +// quick overview of brigadier vs iosonism: +// +// - brigadier.StringReader -> iosonism::strcursor::StringReader + Cursor<&str> +// - brigadier.context.StringRange -> Range +// - brigadier.suggestion.Suggestion -> iosonism::suggestion::Suggestion; + pub mod strcursor; +pub mod suggestion; #[cfg(test)] mod tests { diff --git a/src/suggestion.rs b/src/suggestion.rs new file mode 100644 index 0000000..bad9793 --- /dev/null +++ b/src/suggestion.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2021 Soni L. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +//! A Suggestion. + +use ::std::ops::Range; + +/// A suggested editing operation. +/// +/// # Examples +/// +/// ```rust +/// use iosonism::suggestion::Suggestion; +/// +/// let s = Suggestion::new(3..3, "home".into()); +/// assert_eq!(s.apply("Go ".into()), "Go home"); +/// ``` +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct Suggestion { + range: Range, + text: String, +} + +impl Suggestion { + /// Creates a new `Suggestion` for `text` for the given `range`. + pub fn new(range: Range, text: String) -> Self { + Self { + range: range, + text: text, + } + } + + /// Returns the range associated with this suggestion. + pub fn get_range(&self) -> Range { + self.range.clone() + } + + /// Returns the replacement text associated with this suggestion. + pub fn get_text(&self) -> &str { + &self.text + } + + /// Applies this suggestion on the `input`. + /// + /// # Panics + /// + /// Panics if the range is outside the `input`'s bounds, or not on an UTF-8 + /// boundary. + pub fn apply(&self, mut input: String) -> String { + // the use of String here actually has a performance reason: + // either you already have a String, in which case this is fast, + // or you need a String anyway, in which case it's your responsibility + // to make your &str into one. + input.replace_range(self.range.clone(), &self.text); + input + } + + /// Creates a new `Suggestion` by applying this `Suggestion` on the + /// `range` part of the given `input`. + /// + /// # Panics + /// + /// May panic if this suggestion's range is outside the bounds given by + /// `range`. Panics if the given `range` is outside `input`'s bounds, or any + /// range is not on an UTF-8 boundary. + // FIXME: This function could really use a better description. + pub fn expand(&self, mut input: String, range: Range) -> Self { + // It is our understanding that both ranges must be within input. + // It's also our understanding that self.range must be within the passed + // range. + // So we just do our best to enforce those. + input.truncate(range.end); + input.drain(..range.start); + input.replace_range( + (self.range.start-range.start)..(self.range.end-range.start), + &self.text, + ); + Self::new(range, input) + } +} diff --git a/tests/suggestion.rs b/tests/suggestion.rs new file mode 100644 index 0000000..0e1b9fe --- /dev/null +++ b/tests/suggestion.rs @@ -0,0 +1,89 @@ +// Copyright (c) 2021 Soni L. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +// because we wanna use double underscore (__) for test names +#![allow(non_snake_case)] + +use ::iosonism::suggestion::Suggestion; + +#[test] +fn test_apply__insertion_start() { + let s = Suggestion::new(0..0, "And so I said: ".into()); + assert_eq!(s.apply("Hello world!".into()), "And so I said: Hello world!"); +} + +#[test] +fn test_apply__insertion_middle() { + let s = Suggestion::new(6..6, "small ".into()); + assert_eq!(s.apply("Hello world!".into()), "Hello small world!"); +} + +#[test] +fn test_apply__insertion_end() { + let s = Suggestion::new(5..5, " world!".into()); + assert_eq!(s.apply("Hello".into()), "Hello world!"); +} + +#[test] +fn test_apply__replacement_start() { + let s = Suggestion::new(0..5, "Goodbye".into()); + assert_eq!(s.apply("Hello world!".into()), "Goodbye world!"); +} + +#[test] +fn test_apply__replacement_middle() { + let s = Suggestion::new(6..11, "Alex".into()); + assert_eq!(s.apply("Hello world!".into()), "Hello Alex!"); +} + +#[test] +fn test_apply__replacement_end() { + let s = Suggestion::new(6..12, "Creeper!".into()); + assert_eq!(s.apply("Hello world!".into()), "Hello Creeper!"); +} + +#[test] +fn test_apply__replacement_everything() { + let s = Suggestion::new(0..12, "Oh dear.".into()); + assert_eq!(s.apply("Hello world!".into()), "Oh dear."); +} + +#[test] +fn test_expand__unchanged() { + let s = Suggestion::new(1..1, "oo".into()); + assert_eq!(s.expand("f".into(), 1..1), s); +} + +#[test] +fn test_expand__left() { + let s = Suggestion::new(1..1, "oo".into()); + assert_eq!(s.expand("f".into(), 0..1), Suggestion::new(0..1, "foo".into())); +} + +#[test] +fn test_expand__right() { + let s = Suggestion::new(0..0, "minecraft:".into()); + assert_eq!( + s.expand("fish".into(), 0..4), + Suggestion::new(0..4, "minecraft:fish".into()), + ); +} + +#[test] +fn test_expand__both() { + let s = Suggestion::new(11..11, "minecraft:".into()); + assert_eq!( + s.expand("give Steve fish_block".into(), 5..21), + Suggestion::new(5..21, "Steve minecraft:fish_block".into()), + ); +} + +#[test] +fn test_expand__replacement() { + let s = Suggestion::new(6..11, "strangers".into()); + assert_eq!( + s.expand("Hello world!".into(), 0..12), + Suggestion::new(0..12, "Hello strangers!".into()), + ); +} -- cgit 1.4.1