// AuthoredRun: a run of text attributed to a single author.
// Adjacent runs from the same author merge automatically.
///|
struct AuthoredRun {
author : String
text : String
} derive(Debug, Eq)
///|
impl @rle.Mergeable for AuthoredRun with fn can_merge(
a : AuthoredRun,
b : AuthoredRun,
) -> Bool {
a.author == b.author
}
///|
impl @rle.Mergeable for AuthoredRun with fn merge(
a : AuthoredRun,
b : AuthoredRun,
) -> AuthoredRun {
{ author: a.author, text: a.text + b.text }
}
///|
impl @rle.HasLength for AuthoredRun with fn length(self : AuthoredRun) -> Int {
self.text.length()
}
///|
impl @rle.Spanning for AuthoredRun with fn span(self : AuthoredRun) -> Int {
self.text.length()
}
///|
/// Sliceable via String's built-in Sliceable impl — validates bounds and
/// surrogate pair boundaries automatically.
impl @rle.Sliceable for AuthoredRun with fn slice(
self : AuthoredRun,
start~ : Int,
end~ : Int,
) -> Result[AuthoredRun, @rle.RleError] {
match @rle.Sliceable::slice(self.text, start~, end~) {
Ok(sliced) => Ok({ author: self.author, text: sliced })
Err(e) => Err(e)
}
}
///|
fn authored(author : String, text : String) -> AuthoredRun {
{ author, text }
}
///|
fn print_authored(run : AuthoredRun?) -> Unit {
match run {
Some(run) => println("Some({author: \{run.author}, text: \{run.text}})")
None => println("None")
}
}
///|
fn main {
println("=== Same-Author Merge ===")
let rle : @rle.Rle[AuthoredRun] = @rle.Rle::Rle()
rle.append(authored("alice", "Hello")) |> @debug.debug //=> Ok(())
rle.append(authored("alice", ", world")) |> @debug.debug //=> Ok(())
rle.length() |> println //=> 1 (merged)
print_authored(rle.get(0)) //=> Some({author: "alice", text: "Hello, world"})
println("\n=== Different-Author Separation ===")
let rle2 : @rle.Rle[AuthoredRun] = @rle.Rle::Rle()
rle2.append(authored("alice", "Hello")) |> @debug.debug
rle2.append(authored("bob", " world")) |> @debug.debug
rle2.length() |> println //=> 2 (kept separate)
print_authored(rle2.get(0)) //=> Some({author: "alice", text: "Hello"})
print_authored(rle2.get(1)) //=> Some({author: "bob", text: " world"})
println("\n=== Split at Author Boundary ===")
match rle2.split(5) {
Ok((left, right)) => {
left.length() |> println //=> 1
right.length() |> println //=> 1
print_authored(left.get(0)) //=> Some({author: "alice", text: "Hello"})
print_authored(right.get(0)) //=> Some({author: "bob", text: " world"})
}
Err(e) => e |> @debug.debug
}
println("\n=== Range Across Runs ===")
// "Hello" (alice, 0-4) + " world" (bob, 5-10)
// range(3, 8) -> "lo" from alice, " wo" from bob
match rle2.range(start=3, end=8) {
Ok(iter) =>
iter.each(fn(slice) {
match slice.to_inner() {
Ok(r) => println("\{r.author}: \{r.text}")
Err(e) => e |> @debug.debug
}
})
//=> alice: lo
//=> bob: wo
Err(e) => e |> @debug.debug
}
println("\n=== From Array (same-author merges) ===")
let runs = [
authored("alice", "foo"),
authored("alice", "bar"),
authored("bob", "baz"),
]
let rle3 = @rle.Rle::from_array(runs)
rle3.length() |> println //=> 2 (alice merged, bob separate)
print_authored(rle3.get(0)) //=> Some({author: "alice", text: "foobar"})
print_authored(rle3.get(1)) //=> Some({author: "bob", text: "baz"})
}