sugarloaf 0.3.9

Sugarloaf is Rio rendering engine, designed to be multiplatform. It is based on WebGPU, Rust library for Desktops and WebAssembly for Web (JavaScript). This project is created and maintained for Rio terminal purposes but feel free to use it.
Documentation
// font_introspector was retired from https://github.com/dfrg/swash
// which is licensed under MIT license

//! Simple cluster formation (unicode grapheme cluster algorithm).

use super::super::ClusterBreak;
use super::{CharCluster, Emoji, ShapeClass, Token, Whitespace, MAX_CLUSTER_SIZE};

pub struct SimpleState<I> {
    chars: I,
    cur: Token,
    cur_kind: ClusterBreak,
    cur_emoji: bool,
    done: bool,
}

impl<I> SimpleState<I>
where
    I: Iterator<Item = Token>,
{
    pub fn new(mut chars: I) -> Self {
        if let Some(first) = chars.by_ref().next() {
            let (kind, emoji) = first.info.cluster_class();
            Self {
                chars,
                cur: first,
                cur_kind: kind,
                cur_emoji: emoji,
                done: false,
            }
        } else {
            Self {
                chars,
                cur: Token::default(),
                cur_kind: ClusterBreak::XX,
                cur_emoji: false,
                done: true,
            }
        }
    }

    pub fn next(&mut self, cluster: &mut CharCluster) -> bool {
        if self.done {
            return false;
        }
        Parser { s: self, cluster }.parse();
        true
    }
}

pub struct Parser<'a, I> {
    s: &'a mut SimpleState<I>,
    cluster: &'a mut CharCluster,
}

impl<'a, I> Parser<'a, I>
where
    I: Iterator<Item = Token>,
{
    fn parse(&mut self) -> Option<()> {
        use ClusterBreak::*;
        while self.accept(PP)? {}
        if self.emoji() {
            self.cluster.info_mut().set_emoji(Emoji::Default);
            while self.emoji() {
                self.accept_any()?;
                if !self.parse_emoji_extension()? {
                    break;
                }
            }
        } else {
            match self.kind() {
                CN => {
                    self.accept_any_as(ShapeClass::Control)?;
                }
                LF => {
                    self.cluster.info_mut().set_space(Whitespace::Newline);
                    self.accept_any_as(ShapeClass::Control)?;
                }
                CR => {
                    self.cluster.info_mut().set_space(Whitespace::Newline);
                    self.accept_any_as(ShapeClass::Control)?;
                    self.accept_as(LF, ShapeClass::Control)?;
                }
                L => {
                    self.accept_any()?;
                    match self.kind() {
                        L | V | LV | LVT => {
                            self.accept_any()?;
                        }
                        _ => {}
                    }
                }
                LV | V => {
                    self.accept_any()?;
                    match self.kind() {
                        V | T => {
                            self.accept_any()?;
                        }
                        _ => {}
                    }
                }
                LVT | T => {
                    self.accept_any()?;
                    self.accept(T)?;
                }
                RI => {
                    self.accept(RI)?;
                }
                EX | SM | ZWJ => {
                    self.cluster.info_mut().set_broken();
                }
                _ => {
                    self.cluster.info_mut().set_space_from_char(self.s.cur.ch);
                    self.accept_any()?;
                }
            }
        }
        while self.parse_extension()? {}
        Some(())
    }

    fn parse_emoji_extension(&mut self) -> Option<bool> {
        use ClusterBreak::*;
        loop {
            match self.kind() {
                EX => match self.s.cur.ch as u32 {
                    0x200C => self.accept_any_as(ShapeClass::Zwnj)?,
                    0xFE0F => {
                        self.cluster.info_mut().set_emoji(Emoji::Color);
                        self.cluster.note_char(&self.s.cur);
                        self.advance()?;
                    }
                    0xFE0E => {
                        self.cluster.info_mut().set_emoji(Emoji::Text);
                        self.cluster.note_char(&self.s.cur);
                        self.advance()?;
                    }
                    _ => self.accept_any_as(ShapeClass::Mark)?,
                },
                ZWJ => {
                    self.accept_any_as(ShapeClass::Zwj)?;
                    return Some(true);
                }
                _ => break,
            }
        }
        Some(false)
    }

    fn parse_extension(&mut self) -> Option<bool> {
        use ClusterBreak::*;
        Some(match self.kind() {
            EX => {
                if self.s.cur.ch as u32 == 0x200C {
                    self.accept_any_as(ShapeClass::Zwnj)?;
                } else if self.s.cur.info.is_variation_selector() {
                    self.accept_any_as(ShapeClass::Vs)?;
                } else {
                    self.cluster.force_normalize();
                    self.accept_any_as(ShapeClass::Mark)?;
                }
                true
            }
            SM => {
                self.cluster.force_normalize();
                self.accept_any_as(ShapeClass::Mark)?;
                true
            }
            ZWJ => {
                self.accept_any_as(ShapeClass::Zwj)?;
                true
            }
            _ => false,
        })
    }

    #[inline(always)]
    fn emoji(&self) -> bool {
        self.s.cur_emoji
    }

    #[inline(always)]
    fn kind(&self) -> ClusterBreak {
        self.s.cur_kind
    }

    fn accept(&mut self, kind: ClusterBreak) -> Option<bool> {
        if self.s.cur_kind == kind {
            self.accept_any()?;
            Some(true)
        } else {
            Some(false)
        }
    }

    fn accept_as(&mut self, kind: ClusterBreak, as_kind: ShapeClass) -> Option<bool> {
        if self.s.cur_kind == kind {
            self.accept_any_as(as_kind)?;
            Some(true)
        } else {
            Some(false)
        }
    }

    fn accept_any(&mut self) -> Option<()> {
        self.push_cur();
        self.advance()?;
        Some(())
    }

    fn accept_any_as(&mut self, as_kind: ShapeClass) -> Option<()> {
        self.cluster.push(&self.s.cur, as_kind);
        self.advance()?;
        Some(())
    }

    fn advance(&mut self) -> Option<()> {
        if self.cluster.len() as usize == MAX_CLUSTER_SIZE {
            return None;
        }
        if let Some(input) = self.s.chars.next() {
            let (kind, emoji) = input.info.cluster_class();
            self.s.cur = input;
            self.s.cur_emoji = emoji;
            self.s.cur_kind = kind;
            Some(())
        } else {
            self.s.done = true;
            None
        }
    }

    #[inline]
    fn push_cur(&mut self) {
        self.cluster.push(&self.s.cur, ShapeClass::Base);
    }
}