From 819a108f073c84d835407ea0fe1ff322bd1cf238 Mon Sep 17 00:00:00 2001 From: cN3rd Date: Fri, 3 Sep 2021 18:31:39 +0300 Subject: [PATCH] Add `text_direction` property in general book metadata Text direction can selected in the config via the `text_direction` attribute in `book.toml`, or be derived from the book's language. --- guide/src/format/configuration/general.md | 4 + src/config.rs | 220 ++++++++++++++++++++++ 2 files changed, 224 insertions(+) diff --git a/guide/src/format/configuration/general.md b/guide/src/format/configuration/general.md index 1fc00968..b6dd02d6 100644 --- a/guide/src/format/configuration/general.md +++ b/guide/src/format/configuration/general.md @@ -46,6 +46,9 @@ This is general information about your book. `src` directly under the root folder. But this is configurable with the `src` key in the configuration file. - **language:** The main language of the book, which is used as a language attribute `` for example. + This is also used to derive the direction of text (RTL, LTR) within the book. +- **text_direction**: The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL). Possible values: `ltr`, `rtl`. + When not specified, the correct text direction is derived from the book's `language` attribute. **book.toml** ```toml @@ -55,6 +58,7 @@ authors = ["John Doe", "Jane Doe"] description = "The example book covers examples." src = "my-src" # the source files will be found in `root/my-src` instead of `root/src` language = "en" +text-direction = "ltr" ``` ### Rust options diff --git a/src/config.rs b/src/config.rs index 4641d1a2..8a20e4f3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -411,6 +411,9 @@ pub struct BookConfig { pub multilingual: bool, /// The main language of the book. pub language: Option, + /// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL). + /// When not specified, the correct text direction is derived from [BookConfig::language]. + pub text_direction: Option, } impl Default for BookConfig { @@ -422,6 +425,44 @@ impl Default for BookConfig { src: PathBuf::from("src"), multilingual: false, language: Some(String::from("en")), + text_direction: None, + } + } +} + +impl BookConfig { + /// Gets the realized text direction, either from [BookConfig::text_direction] + /// or derived from [BookConfig::language], to be used by templating engines. + pub fn realized_text_direction(&self) -> TextDirection { + if let Some(direction) = self.text_direction { + direction + } else { + TextDirection::from_lang_code(&self.language.clone().unwrap_or_default()) + } + } +} + +/// Text direction to use for HTML output +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub enum TextDirection { + /// Left to right. + #[serde(rename = "ltr")] + LeftToRight, + /// Right to left + #[serde(rename = "rtl")] + RightToLeft, +} + +impl TextDirection { + /// Gets the text direction from language code + pub fn from_lang_code(code: &str) -> Self { + match code { + // list sourced from here: https://github.com/abarrak/rtl/blob/master/lib/rtl/core.rb#L16 + "ar" | "ara" | "arc" | "ae" | "ave" | "egy" | "he" | "heb" | "nqo" | "pal" | "phn" + | "sam" | "syc" | "syr" | "fa" | "per" | "fas" | "ku" | "kur" | "ur" | "urd" => { + TextDirection::RightToLeft + } + _ => TextDirection::LeftToRight, } } } @@ -788,6 +829,7 @@ mod tests { multilingual: true, src: PathBuf::from("source"), language: Some(String::from("ja")), + text_direction: None, }; let build_should_be = BuildConfig { build_dir: PathBuf::from("outputs"), @@ -1140,6 +1182,184 @@ mod tests { assert_eq!(&get_404_output_file(&html_config.input_404), "missing.html"); } + #[test] + fn text_direction_ltr() { + let src = r#" + [book] + text-direction = "ltr" + "#; + + let got = Config::from_str(src).unwrap(); + assert_eq!(got.book.text_direction, Some(TextDirection::LeftToRight)); + } + + #[test] + fn text_direction_rtl() { + let src = r#" + [book] + text-direction = "rtl" + "#; + + let got = Config::from_str(src).unwrap(); + assert_eq!(got.book.text_direction, Some(TextDirection::RightToLeft)); + } + + #[test] + fn text_direction_none() { + let src = r#" + [book] + "#; + + let got = Config::from_str(src).unwrap(); + assert_eq!(got.book.text_direction, None); + } + + #[test] + fn test_text_direction() { + let mut cfg = BookConfig::default(); + + // test deriving the text direction from language codes + cfg.language = Some("ar".into()); + assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft); + + cfg.language = Some("he".into()); + assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft); + + cfg.language = Some("en".into()); + assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight); + + cfg.language = Some("ja".into()); + assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight); + + // test forced direction + cfg.language = Some("ar".into()); + cfg.text_direction = Some(TextDirection::LeftToRight); + assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight); + + cfg.language = Some("ar".into()); + cfg.text_direction = Some(TextDirection::RightToLeft); + assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft); + + cfg.language = Some("en".into()); + cfg.text_direction = Some(TextDirection::LeftToRight); + assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight); + + cfg.language = Some("en".into()); + cfg.text_direction = Some(TextDirection::RightToLeft); + assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft); + } + + #[test] + fn text_drection_from_lang_code() { + // test all right-to-left languages + assert_eq!( + TextDirection::from_lang_code("ar"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("ara"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("arc"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("ae"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("ave"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("egy"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("he"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("heb"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("nqo"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("pal"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("phn"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("sam"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("syc"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("syr"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("fa"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("per"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("fas"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("ku"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("kur"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("ur"), + TextDirection::RightToLeft + ); + assert_eq!( + TextDirection::from_lang_code("urd"), + TextDirection::RightToLeft + ); + + // test some left-to-right languages + assert_eq!( + TextDirection::from_lang_code("de"), + TextDirection::LeftToRight + ); + assert_eq!( + TextDirection::from_lang_code("en"), + TextDirection::LeftToRight + ); + assert_eq!( + TextDirection::from_lang_code("es"), + TextDirection::LeftToRight + ); + assert_eq!( + TextDirection::from_lang_code("ja"), + TextDirection::LeftToRight + ); + assert_eq!( + TextDirection::from_lang_code("sv"), + TextDirection::LeftToRight + ); + } + #[test] #[should_panic(expected = "Invalid configuration file")] fn invalid_language_type_error() {