#[derive(Clone, Debug, Copy, PartialEq, Eq, Default)]
pub enum TextDirection {
#[default]
LTR,
RTL,
}
impl TextDirection {
pub fn to_xml_attr(&self) -> &'static str {
match self {
TextDirection::LTR => "0",
TextDirection::RTL => "1",
}
}
pub fn is_rtl(&self) -> bool {
matches!(self, TextDirection::RTL)
}
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum RtlLanguage {
Arabic,
Hebrew,
Urdu,
Persian,
Pashto,
Sindhi,
Kurdish,
Yiddish,
}
impl RtlLanguage {
pub fn lang_tag(&self) -> &'static str {
match self {
RtlLanguage::Arabic => "ar-SA",
RtlLanguage::Hebrew => "he-IL",
RtlLanguage::Urdu => "ur-PK",
RtlLanguage::Persian => "fa-IR",
RtlLanguage::Pashto => "ps-AF",
RtlLanguage::Sindhi => "sd-PK",
RtlLanguage::Kurdish => "ku-IQ",
RtlLanguage::Yiddish => "yi-001",
}
}
pub fn default_font(&self) -> &'static str {
match self {
RtlLanguage::Arabic | RtlLanguage::Urdu | RtlLanguage::Persian
| RtlLanguage::Pashto | RtlLanguage::Sindhi | RtlLanguage::Kurdish => "Arial",
RtlLanguage::Hebrew | RtlLanguage::Yiddish => "Arial",
}
}
pub fn direction(&self) -> TextDirection {
TextDirection::RTL
}
}
#[derive(Clone, Debug, Default)]
pub struct RtlTextProps {
pub direction: TextDirection,
pub language: Option<String>,
pub font_complex_script: Option<String>,
}
impl RtlTextProps {
pub fn new() -> Self {
Self::default()
}
pub fn rtl(mut self) -> Self {
self.direction = TextDirection::RTL;
self
}
pub fn ltr(mut self) -> Self {
self.direction = TextDirection::LTR;
self
}
pub fn language(mut self, lang: &str) -> Self {
self.language = Some(lang.to_string());
self
}
pub fn from_language(mut self, lang: RtlLanguage) -> Self {
self.direction = lang.direction();
self.language = Some(lang.lang_tag().to_string());
self.font_complex_script = Some(lang.default_font().to_string());
self
}
pub fn complex_script_font(mut self, font: &str) -> Self {
self.font_complex_script = Some(font.to_string());
self
}
pub fn to_ppr_xml_attr(&self) -> String {
if self.direction.is_rtl() {
r#" rtl="1""#.to_string()
} else {
String::new()
}
}
pub fn to_rpr_xml_attrs(&self) -> String {
let mut attrs = String::new();
if let Some(ref lang) = self.language {
attrs.push_str(&format!(r#" lang="{lang}""#));
}
attrs
}
pub fn to_cs_font_xml(&self) -> String {
if let Some(ref font) = self.font_complex_script {
format!(r#"<a:cs typeface="{}"/>"#, super::escape_xml(font))
} else {
String::new()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_text_direction_default() {
let dir = TextDirection::default();
assert_eq!(dir, TextDirection::LTR);
assert!(!dir.is_rtl());
}
#[test]
fn test_text_direction_rtl() {
let dir = TextDirection::RTL;
assert!(dir.is_rtl());
assert_eq!(dir.to_xml_attr(), "1");
}
#[test]
fn test_text_direction_ltr() {
let dir = TextDirection::LTR;
assert!(!dir.is_rtl());
assert_eq!(dir.to_xml_attr(), "0");
}
#[test]
fn test_rtl_language_arabic() {
let lang = RtlLanguage::Arabic;
assert_eq!(lang.lang_tag(), "ar-SA");
assert_eq!(lang.default_font(), "Arial");
assert_eq!(lang.direction(), TextDirection::RTL);
}
#[test]
fn test_rtl_language_hebrew() {
let lang = RtlLanguage::Hebrew;
assert_eq!(lang.lang_tag(), "he-IL");
assert_eq!(lang.default_font(), "Arial");
}
#[test]
fn test_rtl_language_all_variants() {
let languages = [
RtlLanguage::Arabic, RtlLanguage::Hebrew, RtlLanguage::Urdu,
RtlLanguage::Persian, RtlLanguage::Pashto, RtlLanguage::Sindhi,
RtlLanguage::Kurdish, RtlLanguage::Yiddish,
];
for lang in &languages {
assert_eq!(lang.direction(), TextDirection::RTL);
assert!(!lang.lang_tag().is_empty());
assert!(!lang.default_font().is_empty());
}
}
#[test]
fn test_rtl_text_props_default() {
let props = RtlTextProps::new();
assert_eq!(props.direction, TextDirection::LTR);
assert!(props.language.is_none());
assert!(props.font_complex_script.is_none());
}
#[test]
fn test_rtl_text_props_builder() {
let props = RtlTextProps::new()
.rtl()
.language("ar-SA")
.complex_script_font("Arial");
assert!(props.direction.is_rtl());
assert_eq!(props.language.as_deref(), Some("ar-SA"));
assert_eq!(props.font_complex_script.as_deref(), Some("Arial"));
}
#[test]
fn test_rtl_text_props_from_language() {
let props = RtlTextProps::new().from_language(RtlLanguage::Arabic);
assert!(props.direction.is_rtl());
assert_eq!(props.language.as_deref(), Some("ar-SA"));
assert_eq!(props.font_complex_script.as_deref(), Some("Arial"));
}
#[test]
fn test_rtl_ppr_xml_attr() {
let rtl_props = RtlTextProps::new().rtl();
assert_eq!(rtl_props.to_ppr_xml_attr(), r#" rtl="1""#);
let ltr_props = RtlTextProps::new().ltr();
assert_eq!(ltr_props.to_ppr_xml_attr(), "");
}
#[test]
fn test_rtl_rpr_xml_attrs() {
let props = RtlTextProps::new().language("he-IL");
assert!(props.to_rpr_xml_attrs().contains(r#"lang="he-IL""#));
}
#[test]
fn test_rtl_cs_font_xml() {
let props = RtlTextProps::new().complex_script_font("Arial");
let xml = props.to_cs_font_xml();
assert!(xml.contains(r#"<a:cs typeface="Arial"/>"#));
let empty = RtlTextProps::new();
assert_eq!(empty.to_cs_font_xml(), "");
}
#[test]
fn test_rtl_props_ltr_override() {
let props = RtlTextProps::new().rtl().ltr();
assert!(!props.direction.is_rtl());
}
}