mod detection;
use crate::error::Result;
use crate::rich_text::{RichText, RichTextSegment};
use crate::tests::MockClient;
use atrium_api::app::bsky::richtext::facet::{
ByteSliceData, LinkData, Main, MainData, MainFeaturesItem, MentionData,
};
use atrium_api::types::{Union, UnknownData};
use ipld_core::ipld::Ipld;
pub async fn rich_text_with_detect_facets(text: &str) -> Result<RichText> {
#[cfg(feature = "default-client")]
{
let mut rt = RichText::new(text, None);
rt.detect_facets(MockClient).await?;
Ok(rt)
}
#[cfg(not(feature = "default-client"))]
{
RichText::new_with_detect_facets(text, MockClient).await
}
}
fn facet(byte_start: usize, byte_end: usize) -> Main {
MainData {
features: vec![Union::Unknown(UnknownData { r#type: String::new(), data: Ipld::Null })],
index: ByteSliceData { byte_end, byte_start }.into(),
}
.into()
}
#[test]
fn calculate_bytelength_and_grapheme_length() {
{
let rt = RichText::new("Hello!", None);
assert_eq!(rt.text.len(), 6);
assert_eq!(rt.grapheme_len(), 6);
}
{
let rt = RichText::new("π¨βπ©βπ§βπ§", None);
assert_eq!(rt.text.len(), 25);
assert_eq!(rt.grapheme_len(), 1);
}
{
let rt = RichText::new("π¨βπ©βπ§βπ§π₯ good!β
", None);
assert_eq!(rt.text.len(), 38);
assert_eq!(rt.grapheme_len(), 9);
}
}
#[test]
fn insert() {
let input = &RichText::new("hello world", Some(vec![facet(2, 7)]));
{
let mut input = input.clone();
input.insert(0, "test");
assert_eq!(input.text, "testhello world");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 6);
assert_eq!(facets[0].index.byte_end, 11);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "llo w");
}
{
let mut input = input.clone();
input.insert(4, "test");
assert_eq!(input.text, "helltesto world");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 2);
assert_eq!(facets[0].index.byte_end, 11);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "lltesto w");
}
{
let mut input = input.clone();
input.insert(8, "test");
assert_eq!(input.text, "hello wotestrld");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 2);
assert_eq!(facets[0].index.byte_end, 7);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "llo w");
}
}
#[test]
fn insert_with_fat_unicode() {
let input = &RichText::new(
"oneπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§",
Some(vec![facet(0, 28), facet(29, 57), facet(58, 88)]),
);
{
let mut input = input.clone();
input.insert(0, "test");
assert_eq!(input.text, "testoneπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 3);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "oneπ¨βπ©βπ§βπ§");
assert_eq!(&input.text[facets[1].index.byte_start..facets[1].index.byte_end], "twoπ¨βπ©βπ§βπ§");
assert_eq!(&input.text[facets[2].index.byte_start..facets[2].index.byte_end], "threeπ¨βπ©βπ§βπ§");
}
{
let mut input = input.clone();
input.insert(3, "test");
assert_eq!(input.text, "onetestπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 3);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "onetestπ¨βπ©βπ§βπ§");
assert_eq!(&input.text[facets[1].index.byte_start..facets[1].index.byte_end], "twoπ¨βπ©βπ§βπ§");
assert_eq!(&input.text[facets[2].index.byte_start..facets[2].index.byte_end], "threeπ¨βπ©βπ§βπ§");
}
{
let mut input = input.clone();
input.insert(28, "test");
assert_eq!(input.text, "oneπ¨βπ©βπ§βπ§test twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 3);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "oneπ¨βπ©βπ§βπ§");
assert_eq!(&input.text[facets[1].index.byte_start..facets[1].index.byte_end], "twoπ¨βπ©βπ§βπ§");
assert_eq!(&input.text[facets[2].index.byte_start..facets[2].index.byte_end], "threeπ¨βπ©βπ§βπ§");
}
}
#[test]
fn delete() {
let input = &RichText::new("hello world", Some(vec![facet(2, 7)]));
{
let mut input = input.clone();
input.delete(0, 9);
assert_eq!(input.text, "ld");
let facets = input.facets.expect("facets should exist");
assert!(facets.is_empty());
}
{
let mut input = input.clone();
input.delete(7, 11);
assert_eq!(input.text, "hello w");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 2);
assert_eq!(facets[0].index.byte_end, 7);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "llo w");
}
{
let mut input = input.clone();
input.delete(4, 11);
assert_eq!(input.text, "hell");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 2);
assert_eq!(facets[0].index.byte_end, 4);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "ll");
}
{
let mut input = input.clone();
input.delete(3, 5);
assert_eq!(input.text, "hel world");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 2);
assert_eq!(facets[0].index.byte_end, 5);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "l w");
}
{
let mut input = input.clone();
input.delete(1, 5);
assert_eq!(input.text, "h world");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 1);
assert_eq!(facets[0].index.byte_end, 3);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], " w");
}
{
let mut input = input.clone();
input.delete(0, 2);
assert_eq!(input.text, "llo world");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 0);
assert_eq!(facets[0].index.byte_end, 5);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "llo w");
}
}
#[test]
fn delete_with_fat_unicode() {
let input = &RichText::new("oneπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§", Some(vec![facet(29, 57)]));
{
let mut input = input.clone();
input.delete(28, 58);
assert_eq!(input.text, "oneπ¨βπ©βπ§βπ§threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert!(facets.is_empty());
}
{
let mut input = input.clone();
input.delete(57, 88);
assert_eq!(input.text, "oneπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 29);
assert_eq!(facets[0].index.byte_end, 57);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "twoπ¨βπ©βπ§βπ§");
}
{
let mut input = input.clone();
input.delete(31, 88);
assert_eq!(input.text, "oneπ¨βπ©βπ§βπ§ tw");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 29);
assert_eq!(facets[0].index.byte_end, 31);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "tw");
}
{
let mut input = input.clone();
input.delete(30, 32);
assert_eq!(input.text, "oneπ¨βπ©βπ§βπ§ tπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 29);
assert_eq!(facets[0].index.byte_end, 55);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "tπ¨βπ©βπ§βπ§");
}
{
let mut input = input.clone();
input.delete(28, 31);
assert_eq!(input.text, "oneπ¨βπ©βπ§βπ§oπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 28);
assert_eq!(facets[0].index.byte_end, 54);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "oπ¨βπ©βπ§βπ§");
}
{
let mut input = input.clone();
input.delete(0, 2);
assert_eq!(input.text, "eπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§");
let facets = input.facets.expect("facets should exist");
assert_eq!(facets.len(), 1);
assert_eq!(facets[0].index.byte_start, 27);
assert_eq!(facets[0].index.byte_end, 55);
assert_eq!(&input.text[facets[0].index.byte_start..facets[0].index.byte_end], "twoπ¨βπ©βπ§βπ§");
}
}
#[test]
fn segments() {
{
let input = RichText::new("", None);
assert_eq!(input.segments(), vec![RichTextSegment::new("", None)]);
}
{
let input = RichText::new("hello", None);
assert_eq!(input.segments(), vec![RichTextSegment::new("hello", None)]);
}
{
let input = RichText::new("one two three", Some(vec![facet(4, 7)]));
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("one ", None),
RichTextSegment::new("two", Some(facet(4, 7))),
RichTextSegment::new(" three", None),
]
);
}
{
let input = RichText::new("one two three", Some(vec![facet(0, 7)]));
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("one two", Some(facet(0, 7))),
RichTextSegment::new(" three", None),
]
);
}
{
let input = RichText::new("one two three", Some(vec![facet(4, 13)]));
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("one ", None),
RichTextSegment::new("two three", Some(facet(4, 13))),
]
);
}
{
let input = RichText::new("one two three", Some(vec![facet(0, 13)]));
assert_eq!(
input.segments(),
vec![RichTextSegment::new("one two three", Some(facet(0, 13)))]
);
}
{
let input =
RichText::new("one two three", Some(vec![facet(0, 3), facet(4, 7), facet(8, 13)]));
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("one", Some(facet(0, 3))),
RichTextSegment::new(" ", None),
RichTextSegment::new("two", Some(facet(4, 7))),
RichTextSegment::new(" ", None),
RichTextSegment::new("three", Some(facet(8, 13))),
]
);
}
{
let input = RichText::new(
"oneπ¨βπ©βπ§βπ§ twoπ¨βπ©βπ§βπ§ threeπ¨βπ©βπ§βπ§",
Some(vec![facet(0, 28), facet(29, 57), facet(58, 88)]),
);
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("oneπ¨βπ©βπ§βπ§", Some(facet(0, 28))),
RichTextSegment::new(" ", None),
RichTextSegment::new("twoπ¨βπ©βπ§βπ§", Some(facet(29, 57))),
RichTextSegment::new(" ", None),
RichTextSegment::new("threeπ¨βπ©βπ§βπ§", Some(facet(58, 88))),
]
);
}
{
let input = RichText::new(
"one two three",
Some(vec![
MainData {
features: vec![Union::Refs(MainFeaturesItem::Mention(Box::new(
MentionData { did: "did:plc:123".parse().expect("invalid did") }.into(),
)))],
index: ByteSliceData { byte_end: 3, byte_start: 0 }.into(),
}
.into(),
MainData {
features: vec![Union::Refs(MainFeaturesItem::Link(Box::new(
LinkData { uri: String::from("https://example.com") }.into(),
)))],
index: ByteSliceData { byte_end: 7, byte_start: 4 }.into(),
}
.into(),
facet(8, 13),
]),
);
let segments = input.segments();
assert_eq!(segments.len(), 5);
assert!(segments[0].link().is_none());
assert!(segments[0].mention().is_some());
assert!(segments[1].link().is_none());
assert!(segments[1].mention().is_none());
assert!(segments[2].link().is_some());
assert!(segments[2].mention().is_none());
assert!(segments[3].link().is_none());
assert!(segments[3].mention().is_none());
assert!(segments[4].link().is_none());
assert!(segments[4].mention().is_none());
}
{
let input =
RichText::new("one two three", Some(vec![facet(0, 3), facet(2, 9), facet(8, 13)]));
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("one", Some(facet(0, 3))),
RichTextSegment::new(" two ", None),
RichTextSegment::new("three", Some(facet(8, 13))),
]
);
}
{
let input =
RichText::new("one two three", Some(vec![facet(0, 3), facet(4, 9), facet(8, 13)]));
assert_eq!(
input.segments(),
vec![
RichTextSegment::new("one", Some(facet(0, 3))),
RichTextSegment::new(" ", None),
RichTextSegment::new("two t", Some(facet(4, 9))),
RichTextSegment::new("hree", None),
]
);
}
}