Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jamsocket/fractional_index
A Rust implementation of fractional indexing.
https://github.com/jamsocket/fractional_index
Last synced: about 22 hours ago
JSON representation
A Rust implementation of fractional indexing.
- Host: GitHub
- URL: https://github.com/jamsocket/fractional_index
- Owner: jamsocket
- License: mit
- Created: 2021-09-14T12:41:10.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-09-17T19:52:55.000Z (4 months ago)
- Last Synced: 2025-01-05T10:05:53.122Z (8 days ago)
- Language: Rust
- Size: 53.7 KB
- Stars: 52
- Watchers: 3
- Forks: 4
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# `fractional_index`
[![GitHub Repo stars](https://img.shields.io/github/stars/jamsocket/fractional_index?style=social)](https://github.com/jamsocket/fractional_index)
[![crates.io](https://img.shields.io/crates/v/fractional_index.svg)](https://crates.io/crates/fractional_index)
[![docs.rs](https://img.shields.io/badge/docs-release-brightgreen)](https://docs.rs/fractional_index/)
[![build](https://github.com/jamsocket/fractional_index/actions/workflows/rust.yml/badge.svg)](https://github.com/jamsocket/fractional_index/actions/workflows/rust.yml)
[![dependency status](https://deps.rs/repo/github/jamsocket/fractional_index/status.svg)](https://deps.rs/repo/github/jamsocket/fractional_index)This crate implements fractional indexing, a term coined by Figma in their blog post
[*Realtime Editing of Ordered Sequences*](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/).Specifically, this crate provides a type called `FractionalIndex`. A `FractionalIndex` acts as a
“black box” that is only useful for comparing to
another `FractionalIndex`. A `FractionalIndex` can only be constructed from a default constructor or by
reference to an existing `FractionalIndex`.This is useful as a key in a `BTreeMap` when we want to be able to arbitrarily insert or
re-order elements in a collection, but don't actually care what the key is. It’s also useful for resloving conflicts when a list is modified concurrently by multiple users.## Usage
The API of `FractionalIndex` is very simple:
- `FractionalIndex::default()` creates a new fractional index.
- `FractionalIndex::new_before(a)` creates a fractional index before another.
- `FractionalIndex::new_after(a)` creates a fractional index after another.
- `FractionalIndex::new_between(a, b)` creates a fractional index between two others.```rust
use fractional_index::FractionalIndex;fn main() {
// Construct a fractional index.
let index = FractionalIndex::default();// Construct another fractional index that comes before it.
let before_index = FractionalIndex::new_before(&index);
assert!(before_index < index);// Construct a third fractional index between the other two.
let between_index = FractionalIndex::new_between(
&before_index,
&index
).unwrap();
assert!(before_index < between_index);
assert!(between_index < index);
}
```### Stringification
`FractionalIndexes` are stored as byte strings, but they can be converted to and from strings.
```rust
use fractional_index::FractionalIndex;fn main() {
let a = FractionalIndex::default();
let b = FractionalIndex::new_after(&a);
let c = FractionalIndex::new_between(&a, &b).unwrap();
let c_str = c.to_string();
assert_eq!("817f80", c_str);
let c = FractionalIndex::new_between(&a, &b).unwrap();
}
```The lexicographical order of two stringified `FractionalIndex` values is the same as the order of the unstringified version.
```rust
use fractional_index::FractionalIndex;fn main() {
let a = FractionalIndex::default();
let b = FractionalIndex::new_after(&a);
let c = FractionalIndex::new_between(&a, &b).unwrap();
assert!(a.to_string() < c.to_string());
assert!(c.to_string() < b.to_string());
}
```This is mostly useful when constructing indexes that you need to be able to compare in a language with native string comparison but not bytestring comparison, like JavaScript.
### `::new()` constructor
The `::new()` constructor generalizes over `::default()`, `::new_before()`, `::new_after()`, and `::new_between()`.
```rust
use fractional_index::FractionalIndex;fn main() {
let a = FractionalIndex::default();
let a2 = FractionalIndex::new(None, None).unwrap();
assert_eq!(a, a2);let b = FractionalIndex::new_after(&a);
let b2 = FractionalIndex::new(Some(&a), None).unwrap();
assert_eq!(b, b2);
let c = FractionalIndex::new_between(&a, &b).unwrap();
let c2 = FractionalIndex::new(Some(&a), Some(&b)).unwrap();
assert_eq!(c, c2);
}
```### Serialization
With the `serde` feature (enabled by default), `FractionalIndexes` can be serialized.
```rust
use fractional_index::FractionalIndex;
use serde::{Serialize, Deserialize};#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MyStruct {
a: FractionalIndex,
b: FractionalIndex,
c: FractionalIndex,
}fn main() {
let a = FractionalIndex::default();
let b = FractionalIndex::new_after(&a);
let c = FractionalIndex::new_between(&a, &b).unwrap();let my_struct = MyStruct {
a: a.clone(),
b: b.clone(),
c: c.clone(),
};let json_string = serde_json::to_string(&my_struct).unwrap();
let my_struct_de = serde_json::from_str(&json_string).unwrap();assert_eq!(my_struct, my_struct_de);
}
```By default, `FractionalIndexes` are serialized as byte arrays, which can be serialized efficiently in formats like [bincode](https://github.com/bincode-org/bincode).
These byte arrays are less useful when using a format like JSON to send data to a non-Rust programming language.
For these use cases, we provide a stringifying serializer which can be enabled by annotating a field with `#[serde(with="fractional_index::stringify")]`.
```rust
use fractional_index::FractionalIndex;
use serde::{Serialize, Deserialize};
use serde_json::json;#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MyStruct {
#[serde(with="fractional_index::stringify")]
a: FractionalIndex,
#[serde(with="fractional_index::stringify")]
b: FractionalIndex,
#[serde(with="fractional_index::stringify")]
c: FractionalIndex,
}fn main() {
let a = FractionalIndex::default();
let b = FractionalIndex::new_after(&a);
let c = FractionalIndex::new_between(&a, &b).unwrap();let my_struct = MyStruct {
a: a.clone(),
b: b.clone(),
c: c.clone(),
};let json_value = serde_json::to_value(&my_struct).unwrap();
let expected = json!({
"a": "80",
"b": "8180",
"c": "817f80",
});assert_eq!(expected, json_value);
}
```## Stability
The byte representation of a `FractionalIndex` can be relied upon to be fully forward- and backward-compatible with future versions of this crate, meaning that the serialized representation of two `FractionalIndex`es produced by any version of this crate will compare the same way when deserialized in any other version.
The actual byte representation of `FractionalIndex`es created by `new_before`, `new_after`, and `new_between` may differ between versions, but the result will always compare appropriately with the reference `FractionalIndex`(es) used for construction regardless of version.
The byte representation of a `FractionalIndex` is **not** meant to be compatible with the byte representaiton of a `ZenoIndex`, nor are their serialized counterparts.
## Version 2.x.x note
In version 1.x.x of this crate, fractional indexing was implemented through a struct called `ZenoIndex`. The implementation of `ZenoIndex` is similar to `FractionalIndex`, and they both represent the underlying data as a byte string, but `ZenoIndex` requires a custom comparison function to be used with that byte string. `FractionalIndex` changes the byte representation so that a lexicographical comparison of the underlying byte data is all that is required to compare two `FractionalIndex`es.
The `ZenoIndex` struct is still available in version 2.x.x of this crate, but it is deprecated. New code should use `FractionalIndex` instead, which implements the same functionality.