decasify 0.11.0__tar.gz → 0.11.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,22 +67,22 @@ dependencies = [
67
67
 
68
68
  [[package]]
69
69
  name = "anstyle-query"
70
- version = "1.1.4"
70
+ version = "1.1.5"
71
71
  source = "registry+https://github.com/rust-lang/crates.io-index"
72
- checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
72
+ checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
73
73
  dependencies = [
74
- "windows-sys 0.60.2",
74
+ "windows-sys 0.61.2",
75
75
  ]
76
76
 
77
77
  [[package]]
78
78
  name = "anstyle-wincon"
79
- version = "3.0.10"
79
+ version = "3.0.11"
80
80
  source = "registry+https://github.com/rust-lang/crates.io-index"
81
- checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
81
+ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
82
82
  dependencies = [
83
83
  "anstyle",
84
84
  "once_cell_polyfill",
85
- "windows-sys 0.60.2",
85
+ "windows-sys 0.61.2",
86
86
  ]
87
87
 
88
88
  [[package]]
@@ -175,9 +175,9 @@ dependencies = [
175
175
 
176
176
  [[package]]
177
177
  name = "cc"
178
- version = "1.2.45"
178
+ version = "1.2.46"
179
179
  source = "registry+https://github.com/rust-lang/crates.io-index"
180
- checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
180
+ checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
181
181
  dependencies = [
182
182
  "find-msvc-tools",
183
183
  "shlex",
@@ -191,9 +191,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
191
191
 
192
192
  [[package]]
193
193
  name = "clap"
194
- version = "4.5.51"
194
+ version = "4.5.52"
195
195
  source = "registry+https://github.com/rust-lang/crates.io-index"
196
- checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
196
+ checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8"
197
197
  dependencies = [
198
198
  "clap_builder",
199
199
  "clap_derive",
@@ -201,9 +201,9 @@ dependencies = [
201
201
 
202
202
  [[package]]
203
203
  name = "clap_builder"
204
- version = "4.5.51"
204
+ version = "4.5.52"
205
205
  source = "registry+https://github.com/rust-lang/crates.io-index"
206
- checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
206
+ checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1"
207
207
  dependencies = [
208
208
  "anstream",
209
209
  "anstyle",
@@ -307,7 +307,7 @@ dependencies = [
307
307
 
308
308
  [[package]]
309
309
  name = "decasify"
310
- version = "0.11.0"
310
+ version = "0.11.2"
311
311
  dependencies = [
312
312
  "anyhow",
313
313
  "assert_cmd",
@@ -435,9 +435,9 @@ dependencies = [
435
435
 
436
436
  [[package]]
437
437
  name = "find-msvc-tools"
438
- version = "0.1.4"
438
+ version = "0.1.5"
439
439
  source = "registry+https://github.com/rust-lang/crates.io-index"
440
- checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
440
+ checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
441
441
 
442
442
  [[package]]
443
443
  name = "flate2"
@@ -1977,7 +1977,7 @@ dependencies = [
1977
1977
 
1978
1978
  [[package]]
1979
1979
  name = "typst"
1980
- version = "0.11.0"
1980
+ version = "0.11.2"
1981
1981
  dependencies = [
1982
1982
  "anyhow",
1983
1983
  "decasify",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "decasify"
3
- version = "0.11.0"
3
+ version = "0.11.2"
4
4
  description = "A CLI utility and library to cast strings to title-case according to locale specific style guides including Turkish support"
5
5
  readme = "README.md"
6
6
  build = "build-aux/build.rs"
@@ -13,7 +13,7 @@ repository.workspace = true
13
13
  license.workspace = true
14
14
 
15
15
  [workspace.package]
16
- version = "0.11.0"
16
+ version = "0.11.2"
17
17
  authors = ["Caleb Maclennan <caleb@alerque.com>"]
18
18
  homepage = "https://github.com/alerque/decasify"
19
19
  repository = "https://github.com/alerque/decasify"
@@ -64,7 +64,7 @@ wasm = ["dep:wasm-bindgen"]
64
64
 
65
65
  [workspace.dependencies.decasify]
66
66
  path = "."
67
- version = "0.11.0"
67
+ version = "0.11.2"
68
68
 
69
69
  [dependencies]
70
70
  regex = "1.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: decasify
3
- Version: 0.11.0
3
+ Version: 0.11.2
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Natural Language :: English
@@ -23,6 +23,10 @@ The CLI defaults to titlecase and English, but lower, upper, and sentence case o
23
23
  The Rust, Lua, Python, and JavaScript library APIs have functions specific to each operation.
24
24
  Where possible the APIs currently default to English rules and (for English) the Gruber style guide, but others are available.
25
25
 
26
+ The Spanish style roughly follows [optional stylistic exceptions](https://www.rae.es/dpd/may%C3%BAsculas) noted by Real Academia Española.
27
+ Keep in mind most Spanish style guides eschew title casing and use sentence-case for many things that would traditionally be title-cased in English.
28
+ This library implements a best-guess at title-casing when asked to, it does not help you understand when (not) to use it in the first place.
29
+
26
30
  The Turkish style follows the Turkish Language Institute's [guidelines][tdk].
27
31
 
28
32
  For English, three style guides are known: Associated Press (AP), Chicago Manual of Style (CMOS), and John Gruber's Daring Fireball (Gruber).
@@ -259,7 +263,7 @@ The [decasify](https://typst.app/universe/package/decasify) package can be added
259
263
  The exact version must be specified explicitly:
260
264
 
261
265
  ```typst
262
- #import "@preview/decasify:0.11.0": *
266
+ #import "@preview/decasify:0.11.2": *
263
267
  ```
264
268
 
265
269
  Specific functions for each case should be available throughout the document.
@@ -0,0 +1,97 @@
1
+ // SPDX-FileCopyrightText: © 2023 Caleb Maclennan <caleb@alerque.com>
2
+ // SPDX-License-Identifier: LGPL-3.0-only
3
+
4
+ use crate::content::{Chunk, Segment};
5
+ use crate::get_override;
6
+ use crate::types::{StyleGuide, StyleOptions, Word};
7
+
8
+ use unicode_titlecase::StrTitleCase;
9
+
10
+ pub fn titlecase(chunk: Chunk, style: StyleGuide, opts: StyleOptions) -> String {
11
+ let articles_prepositions_conjunctions = [
12
+ "a", "al", "ante", "bajo", "con", "contra", "de", "del", "desde", "durante", "e", "el",
13
+ "en", "entre", "hacia", "hasta", "la", "las", "los", "mas", "mediante", "ni", "o", "para",
14
+ "pero", "por", "que", "según", "si", "sin", "so", "sino", "sobre", "tras", "u", "un",
15
+ "una", "unas", "unos", "y",
16
+ ];
17
+ let determiners = [
18
+ "mi", "mis", "nuestro", "nuestra", "nuestros", "nuestras", "tu", "tus", "vuestro",
19
+ "vuestra", "vuestros", "vuestras", "su", "sus",
20
+ ];
21
+ match style {
22
+ StyleGuide::LanguageDefault => {
23
+ titlecase_spanish(chunk, opts, &articles_prepositions_conjunctions)
24
+ }
25
+ StyleGuide::RealAcademiaEspanola => {
26
+ titlecase_spanish(chunk, opts, &articles_prepositions_conjunctions)
27
+ }
28
+ StyleGuide::FundeuRealAcademiaEspanola => {
29
+ let mut combined = articles_prepositions_conjunctions.to_vec();
30
+ combined.extend_from_slice(&determiners);
31
+ titlecase_spanish(chunk, opts, &combined)
32
+ }
33
+ _ => todo!("Spanish implementation doesn't support this style guide."),
34
+ }
35
+ }
36
+
37
+ fn titlecase_spanish(chunk: Chunk, opts: StyleOptions, reserved_words: &[&str]) -> String {
38
+ let mut chunk = chunk.clone();
39
+ let mut done_first = false;
40
+ chunk.segments.iter_mut().for_each(|segment| {
41
+ if let Segment::Word(word) = segment {
42
+ word.word =
43
+ if let Some(word) = get_override(word, &opts.overrides, |w| w.to_lowercase()) {
44
+ word.to_string()
45
+ } else if !done_first {
46
+ done_first = true;
47
+ word.to_titlecase_lower_rest()
48
+ } else {
49
+ match is_reserved(word, reserved_words) {
50
+ true => word.word.to_lowercase(),
51
+ false => word.word.to_titlecase_lower_rest(),
52
+ }
53
+ }
54
+ }
55
+ });
56
+ chunk.into()
57
+ }
58
+
59
+ fn is_reserved(word: &Word, reserved_words: &[&str]) -> bool {
60
+ reserved_words.contains(&word.word.to_lowercase().as_str())
61
+ }
62
+
63
+ pub fn lowercase(chunk: Chunk) -> String {
64
+ let mut chunk = chunk.clone();
65
+ chunk.segments.iter_mut().for_each(|segment| {
66
+ if let Segment::Word(word) = segment {
67
+ word.word = word.to_lowercase()
68
+ }
69
+ });
70
+ chunk.into()
71
+ }
72
+
73
+ pub fn uppercase(chunk: Chunk) -> String {
74
+ let mut chunk = chunk.clone();
75
+ chunk.segments.iter_mut().for_each(|segment| {
76
+ if let Segment::Word(word) = segment {
77
+ word.word = word.to_uppercase()
78
+ }
79
+ });
80
+ chunk.into()
81
+ }
82
+
83
+ pub fn sentencecase(chunk: Chunk) -> String {
84
+ let mut chunk = chunk.clone();
85
+ let mut done_first = false;
86
+ chunk.segments.iter_mut().for_each(|segment| {
87
+ if let Segment::Word(word) = segment {
88
+ word.word = if !done_first {
89
+ done_first = true;
90
+ word.to_titlecase_lower_rest()
91
+ } else {
92
+ word.to_lowercase()
93
+ }
94
+ }
95
+ });
96
+ chunk.into()
97
+ }
@@ -31,6 +31,7 @@ pub mod python;
31
31
  pub mod wasm;
32
32
 
33
33
  mod en;
34
+ mod es;
34
35
  mod tr;
35
36
 
36
37
  /// Convert a string to a specific case following typesetting conventions for a target locale
@@ -85,6 +86,7 @@ where
85
86
  let opts: StyleOptions = opts.try_into()?;
86
87
  Ok(match locale {
87
88
  Locale::EN => en::titlecase(chunk, style, opts),
89
+ Locale::ES => es::titlecase(chunk, style, opts),
88
90
  Locale::TR => tr::titlecase(chunk, style, opts),
89
91
  })
90
92
  }
@@ -99,6 +101,7 @@ where
99
101
  let locale: Locale = locale.try_into()?;
100
102
  Ok(match locale {
101
103
  Locale::EN => en::lowercase(chunk),
104
+ Locale::ES => es::lowercase(chunk),
102
105
  Locale::TR => tr::lowercase(chunk),
103
106
  })
104
107
  }
@@ -113,6 +116,7 @@ where
113
116
  let locale: Locale = locale.try_into()?;
114
117
  Ok(match locale {
115
118
  Locale::EN => en::uppercase(chunk),
119
+ Locale::ES => es::uppercase(chunk),
116
120
  Locale::TR => tr::uppercase(chunk),
117
121
  })
118
122
  }
@@ -127,6 +131,7 @@ where
127
131
  let locale: Locale = locale.try_into()?;
128
132
  Ok(match locale {
129
133
  Locale::EN => en::sentencecase(chunk),
134
+ Locale::ES => es::sentencecase(chunk),
130
135
  Locale::TR => tr::sentencecase(chunk),
131
136
  })
132
137
  }
@@ -53,6 +53,10 @@ pub struct Word {
53
53
  pub word: String,
54
54
  }
55
55
 
56
+ // WARNING: These enums can't change order when adding new variants because some modules cast them
57
+ // to integers, and that would make for ABI breakage. The variants can be re-sorted (alphabetically
58
+ // or logically or whatever) when a major version with no ABI compatibility guarantees is okay.
59
+
56
60
  /// Locale selector to change language support rules of case functions.
57
61
  #[derive(Default, Display, VariantNames, Debug, Clone, Copy, PartialEq)]
58
62
  #[cfg_attr(feature = "pythonmodule", pyclass(eq))]
@@ -63,6 +67,7 @@ pub enum Locale {
63
67
  #[default]
64
68
  EN,
65
69
  TR,
70
+ ES,
66
71
  }
67
72
 
68
73
  /// Target case selector.
@@ -98,6 +103,10 @@ pub enum StyleGuide {
98
103
  LanguageDefault,
99
104
  #[strum(serialize = "tdk")]
100
105
  TurkishLanguageInstitute,
106
+ #[strum(serialize = "rae")]
107
+ RealAcademiaEspanola,
108
+ #[strum(serialize = "fundeu")]
109
+ FundeuRealAcademiaEspanola,
101
110
  }
102
111
 
103
112
  #[derive(Clone, Debug, Default, PartialEq)]
@@ -180,7 +189,8 @@ impl FromStr for Locale {
180
189
  fn from_str(s: &str) -> Result<Self> {
181
190
  match s.to_ascii_lowercase().as_str() {
182
191
  "en" | "english" | "en_en" => Ok(Locale::EN),
183
- "tr" | "turkish" | "tr_tr" | "türkçe" => Ok(Locale::TR),
192
+ "es" | "spanish" | "es_es" | "espanol" | "español" => Ok(Locale::ES),
193
+ "tr" | "turkish" | "tr_tr" | "turkce" | "türkçe" => Ok(Locale::TR),
184
194
  input => LocaleSnafu { input }.fail()?,
185
195
  }
186
196
  }
@@ -266,6 +276,8 @@ impl FromStr for StyleGuide {
266
276
  "daringfireball" | "gruber" | "fireball" => Ok(StyleGuide::DaringFireball),
267
277
  "associatedpress" | "ap" => Ok(StyleGuide::AssociatedPress),
268
278
  "chicagoManualofstyle" | "chicago" | "cmos" => Ok(StyleGuide::ChicagoManualOfStyle),
279
+ "fundeu" | "fundeurealacademiaespanola" => Ok(StyleGuide::FundeuRealAcademiaEspanola),
280
+ "rae" | "realacademiaespanola" => Ok(StyleGuide::RealAcademiaEspanola),
269
281
  "tdk" | "turkishlanguageinstitute" => Ok(StyleGuide::TurkishLanguageInstitute),
270
282
  "default" | "languagedefault" | "language" | "none" | "" => {
271
283
  Ok(StyleGuide::LanguageDefault)
@@ -90,6 +90,16 @@ case!(
90
90
  " Foo Bar "
91
91
  );
92
92
 
93
+ case!(
94
+ trivia_es,
95
+ Case::Title,
96
+ Locale::ES,
97
+ StyleGuide::LanguageDefault,
98
+ StyleOptions::default(),
99
+ " foo bar ",
100
+ " Foo Bar "
101
+ );
102
+
93
103
  case!(
94
104
  trivia_tr,
95
105
  Case::Title,
@@ -199,6 +209,51 @@ titlecase!(
199
209
  " Free Trolling\n Space "
200
210
  );
201
211
 
212
+ titlecase!(
213
+ rae_articles,
214
+ Locale::ES,
215
+ StyleGuide::LanguageDefault,
216
+ StyleOptions::default(),
217
+ "el libro del autor",
218
+ "El Libro del Autor"
219
+ );
220
+
221
+ titlecase!(
222
+ rae_holiday,
223
+ Locale::ES,
224
+ StyleGuide::RealAcademiaEspanola,
225
+ StyleOptions::default(),
226
+ "DÍA DE los muertos",
227
+ "Día de los Muertos"
228
+ );
229
+
230
+ titlecase!(
231
+ rae_magazine,
232
+ Locale::ES,
233
+ StyleGuide::RealAcademiaEspanola,
234
+ StyleOptions::default(),
235
+ "cien años DE soledad",
236
+ "Cien Años de Soledad"
237
+ );
238
+
239
+ titlecase!(
240
+ rae_prepositions,
241
+ Locale::ES,
242
+ StyleGuide::LanguageDefault,
243
+ StyleOptions::default(),
244
+ "en la casa de mi madre",
245
+ "En la Casa de Mi Madre"
246
+ );
247
+
248
+ titlecase!(
249
+ fundu_prepositions,
250
+ Locale::ES,
251
+ StyleGuide::FundeuRealAcademiaEspanola,
252
+ StyleOptions::default(),
253
+ "en la casa de mi madre",
254
+ "En la Casa de mi Madre"
255
+ );
256
+
202
257
  titlecase!(
203
258
  turkish_question,
204
259
  Locale::TR,
@@ -256,6 +311,8 @@ macro_rules! lowercase {
256
311
 
257
312
  lowercase!(lower_en, Locale::EN, "foo BAR BaZ BIKE", "foo bar baz bike");
258
313
 
314
+ lowercase!(lower_es, Locale::ES, "Hola MUNDO", "hola mundo");
315
+
259
316
  lowercase!(
260
317
  lower_tr,
261
318
  Locale::TR,
@@ -275,6 +332,8 @@ macro_rules! uppercase {
275
332
 
276
333
  uppercase!(upper_en, Locale::EN, "foo BAR BaZ bike", "FOO BAR BAZ BIKE");
277
334
 
335
+ uppercase!(upper_es, Locale::ES, "hola MUNDo", "HOLA MUNDO");
336
+
278
337
  uppercase!(
279
338
  upper_tr,
280
339
  Locale::TR,
@@ -299,4 +358,6 @@ sentencecase!(
299
358
  "Insert bike here"
300
359
  );
301
360
 
361
+ sentencecase!(sentence_es, Locale::ES, "hola MUNDO", "Hola mundo");
362
+
302
363
  sentencecase!(sentence_tr, Locale::TR, "ilk DAVRANSIN", "İlk davransın");
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes