decasify 0.10.2__tar.gz → 0.11.0__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.
@@ -307,7 +307,7 @@ dependencies = [
307
307
 
308
308
  [[package]]
309
309
  name = "decasify"
310
- version = "0.10.2"
310
+ version = "0.11.0"
311
311
  dependencies = [
312
312
  "anyhow",
313
313
  "assert_cmd",
@@ -1977,7 +1977,7 @@ dependencies = [
1977
1977
 
1978
1978
  [[package]]
1979
1979
  name = "typst"
1980
- version = "0.10.2"
1980
+ version = "0.11.0"
1981
1981
  dependencies = [
1982
1982
  "anyhow",
1983
1983
  "decasify",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "decasify"
3
- version = "0.10.2"
3
+ version = "0.11.0"
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.10.2"
16
+ version = "0.11.0"
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.10.2"
67
+ version = "0.11.0"
68
68
 
69
69
  [dependencies]
70
70
  regex = "1.11"
@@ -167,6 +167,7 @@ extend-ignore-identifiers-re = ["[bB][aA][zZ]"]
167
167
 
168
168
  [package.metadata.typos.default.extend-words]
169
169
  runing = "running"
170
+ walm = "wasm"
170
171
 
171
172
  [package.metadata.typos.files]
172
173
  ignore-hidden = false
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: decasify
3
- Version: 0.10.2
3
+ Version: 0.11.0
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Natural Language :: English
@@ -92,7 +92,7 @@ In your `Cargo.toml` file.
92
92
 
93
93
  ```toml
94
94
  [dependencies]
95
- decasify = "0.10"
95
+ decasify = "0.11"
96
96
  ```
97
97
 
98
98
  Then use the crate functions and types in your project something like this:
@@ -103,10 +103,10 @@ use decasify::{Locale, StyleGuide, StyleOptions};
103
103
 
104
104
  fn demo() {
105
105
  let input = "ILIK SU VE İTEN RÜZGARLAR";
106
- let output = titlecase(input, Locale::TR, StyleGuide::LanguageDefault, StyleOptions::default());
106
+ let output = titlecase(input, Locale::TR, StyleGuide::LanguageDefault, StyleOptions::default()).unwrap();
107
107
  eprintln! {"{output}"};
108
108
  let input = "title with a twist: a colon";
109
- let output = titlecase(input, Locale::EN, StyleGuide::DaringFireball, StyleOptions::default());
109
+ let output = titlecase(input, Locale::EN, StyleGuide::DaringFireball, StyleOptions::default()).unwrap();
110
110
  eprintln! {"{output}"};
111
111
  }
112
112
  ```
@@ -259,7 +259,7 @@ The [decasify](https://typst.app/universe/package/decasify) package can be added
259
259
  The exact version must be specified explicitly:
260
260
 
261
261
  ```typst
262
- #import "@preview/decasify:0.10.2": *
262
+ #import "@preview/decasify:0.11.0": *
263
263
  ```
264
264
 
265
265
  Specific functions for each case should be available throughout the document.
@@ -1,53 +1,30 @@
1
1
  // SPDX-FileCopyrightText: © 2023 Caleb Maclennan <caleb@alerque.com>
2
2
  // SPDX-License-Identifier: LGPL-3.0-only
3
3
 
4
- use decasify::cli::Cli;
5
- use decasify::{lowercase, sentencecase, titlecase, uppercase};
6
- use decasify::{Case, Locale, StyleGuide, StyleOptions, StyleOptionsBuilder};
7
-
8
- use snafu::prelude::*;
9
-
10
4
  use clap::CommandFactory;
11
5
  use std::io;
12
6
  use std::io::BufRead;
13
7
 
14
- #[derive(Snafu)]
15
- enum Error {
16
- #[snafu(display("Failed to identify input"))]
17
- Input {},
18
-
19
- #[snafu(display("Failed to resolve a known locale"))]
20
- Locale {},
21
-
22
- #[snafu(display("Failed to resolve a known case"))]
23
- Case {},
24
-
25
- #[snafu(display("Failed to resolve a known style guide"))]
26
- StyleGuide {},
27
- }
28
-
29
- // Clap CLI errors are reported using the Debug trait, but Snafu sets up the Display trait.
30
- // So we delegate. c.f. https://github.com/shepmaster/snafu/issues/110
31
- impl std::fmt::Debug for Error {
32
- fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
33
- std::fmt::Display::fmt(self, fmt)
34
- }
35
- }
36
-
37
- type Result<T, E = Error> = std::result::Result<T, E>;
8
+ use decasify::cli::Cli;
9
+ use decasify::types::Result;
10
+ use decasify::{lowercase, sentencecase, titlecase, uppercase};
11
+ use decasify::{Case, Locale, StyleGuide, StyleOptions, StyleOptionsBuilder};
38
12
 
39
13
  fn main() -> Result<()> {
40
14
  let version = option_env!("VERGEN_GIT_DESCRIBE").unwrap_or_else(|| env!("CARGO_PKG_VERSION"));
41
15
  let app = Cli::command().version(version);
42
16
  let matches = app.get_matches();
43
- let locale = matches.get_one::<Locale>("locale").context(LocaleSnafu)?;
17
+ let locale = matches
18
+ .get_one::<Locale>("locale")
19
+ .unwrap_or(&Locale::default())
20
+ .to_owned();
44
21
  let case = matches
45
22
  .get_one::<Case>("case")
46
- .context(CaseSnafu)?
23
+ .unwrap_or(&Case::default())
47
24
  .to_owned();
48
25
  let style = matches
49
26
  .get_one::<StyleGuide>("style")
50
- .context(StyleGuideSnafu)?
27
+ .unwrap_or(&StyleGuide::default())
51
28
  .to_owned();
52
29
  let opts = if let Some(overrides) = matches.get_many::<String>("overrides") {
53
30
  StyleOptionsBuilder::new()
@@ -60,27 +37,23 @@ fn main() -> Result<()> {
60
37
  true => {
61
38
  let input: Vec<String> = matches
62
39
  .get_many::<String>("input")
63
- .context(InputSnafu)?
40
+ .unwrap()
64
41
  .cloned()
65
42
  .collect();
66
43
  let input: Vec<String> = vec![input.join(" ")];
67
44
  process(
68
45
  input.iter().map(|ln| ln.to_string()),
69
- *locale,
46
+ locale,
70
47
  case,
71
48
  style,
72
49
  opts,
73
- );
50
+ )
51
+ }
52
+ false => {
53
+ let stdin = io::stdin().lock().lines().map(|ln| ln.unwrap());
54
+ process(stdin, locale, case, style, opts)
74
55
  }
75
- false => process(
76
- io::stdin().lock().lines().map(|ln| ln.unwrap()),
77
- *locale,
78
- case,
79
- style,
80
- opts,
81
- ),
82
56
  }
83
- Ok(())
84
57
  }
85
58
 
86
59
  fn process<I: IntoIterator<Item = String>>(
@@ -89,15 +62,16 @@ fn process<I: IntoIterator<Item = String>>(
89
62
  case: Case,
90
63
  style: StyleGuide,
91
64
  opts: StyleOptions,
92
- ) {
65
+ ) -> Result<()> {
93
66
  for string in strings {
94
67
  let output = match case {
95
- Case::Title => titlecase(string, locale, style.clone(), opts.clone()),
96
- Case::Lower => lowercase(string, locale),
97
- Case::Upper => uppercase(string, locale),
98
- Case::Sentence => sentencecase(string, locale),
68
+ Case::Title => titlecase(string, locale, style.clone(), opts.clone())?,
69
+ Case::Lower => lowercase(string, locale)?,
70
+ Case::Upper => uppercase(string, locale)?,
71
+ Case::Sentence => sentencecase(string, locale)?,
99
72
  _ => unreachable!(),
100
73
  };
101
- println!("{output}")
74
+ println!("{output}");
102
75
  }
76
+ Ok(())
103
77
  }
@@ -1,13 +1,11 @@
1
1
  // SPDX-FileCopyrightText: © 2023 Caleb Maclennan <caleb@alerque.com>
2
2
  // SPDX-License-Identifier: LGPL-3.0-only
3
3
 
4
- pub use crate::types::Word;
5
-
6
4
  use regex::Regex;
7
5
  use std::{borrow::Cow, fmt, fmt::Display, str::FromStr};
8
6
  use unicode_titlecase::StrTitleCase;
9
7
 
10
- use snafu::prelude::*;
8
+ use crate::types::{Error, Result, Word};
11
9
 
12
10
  #[derive(Clone, Debug)]
13
11
  #[non_exhaustive]
@@ -22,22 +20,6 @@ pub enum Segment {
22
20
  Word(Word),
23
21
  }
24
22
 
25
- #[derive(Snafu)]
26
- pub enum Error {
27
- #[snafu(display("Unable to cast str to Chunk"))]
28
- StrToChunk {},
29
- }
30
-
31
- // Clap CLI errors are reported using the Debug trait, but Snafu sets up the Display trait.
32
- // So we delegate. c.f. https://github.com/shepmaster/snafu/issues/110
33
- impl std::fmt::Debug for Error {
34
- fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
35
- std::fmt::Display::fmt(self, fmt)
36
- }
37
- }
38
-
39
- pub type Result<T, E = Error> = std::result::Result<T, E>;
40
-
41
23
  fn split_chunk(s: &str) -> Chunk {
42
24
  let mut segments: Vec<Segment> = Vec::new();
43
25
  let captures = Regex::new(r"(?<separator>\p{Whitespace}+)|(?<word>\P{Whitespace}+)").unwrap();
@@ -6,12 +6,13 @@
6
6
 
7
7
  mod content;
8
8
  mod traits;
9
- mod types;
9
+ pub mod types;
10
10
 
11
11
  pub use content::Chunk;
12
12
  #[cfg(feature = "unstable-trait")]
13
13
  pub use traits::Decasify;
14
14
  pub use types::{Case, Locale, StyleGuide, StyleOptions, StyleOptionsBuilder, Word};
15
+ pub use types::{Error, Result};
15
16
 
16
17
  #[cfg(feature = "cli")]
17
18
  #[doc(hidden)]
@@ -33,18 +34,28 @@ mod en;
33
34
  mod tr;
34
35
 
35
36
  /// Convert a string to a specific case following typesetting conventions for a target locale
36
- pub fn case(
37
+ pub fn case<TC, TL, TS, TO>(
37
38
  chunk: impl Into<Chunk>,
38
- case: impl Into<Case>,
39
- locale: impl Into<Locale>,
40
- style: impl Into<StyleGuide>,
41
- opts: impl Into<StyleOptions>,
42
- ) -> String {
39
+ case: TC,
40
+ locale: TL,
41
+ style: TS,
42
+ opts: TO,
43
+ ) -> Result<String>
44
+ where
45
+ TC: TryInto<Case>,
46
+ TL: TryInto<Locale>,
47
+ TS: TryInto<StyleGuide>,
48
+ TO: TryInto<StyleOptions>,
49
+ Error: From<TC::Error>,
50
+ Error: From<TL::Error>,
51
+ Error: From<TS::Error>,
52
+ Error: From<TO::Error>,
53
+ {
43
54
  let chunk: Chunk = chunk.into();
44
- let case: Case = case.into();
45
- let locale: Locale = locale.into();
46
- let style: StyleGuide = style.into();
47
- let opts: StyleOptions = opts.into();
55
+ let case: Case = case.try_into()?;
56
+ let locale: Locale = locale.try_into()?;
57
+ let style: StyleGuide = style.try_into()?;
58
+ let opts: StyleOptions = opts.try_into()?;
48
59
  match case {
49
60
  Case::Lower => lowercase(chunk, locale),
50
61
  Case::Upper => uppercase(chunk, locale),
@@ -54,50 +65,70 @@ pub fn case(
54
65
  }
55
66
 
56
67
  /// Convert a string to title case following typesetting conventions for a target locale
57
- pub fn titlecase(
68
+ pub fn titlecase<TL, TS, TO>(
58
69
  chunk: impl Into<Chunk>,
59
- locale: impl Into<Locale>,
60
- style: impl Into<StyleGuide>,
61
- opts: impl Into<StyleOptions>,
62
- ) -> String {
70
+ locale: TL,
71
+ style: TS,
72
+ opts: TO,
73
+ ) -> Result<String>
74
+ where
75
+ TL: TryInto<Locale>,
76
+ TS: TryInto<StyleGuide>,
77
+ TO: TryInto<StyleOptions>,
78
+ Error: From<TL::Error>,
79
+ Error: From<TS::Error>,
80
+ Error: From<TO::Error>,
81
+ {
63
82
  let chunk: Chunk = chunk.into();
64
- let locale: Locale = locale.into();
65
- let style: StyleGuide = style.into();
66
- let opts: StyleOptions = opts.into();
67
- match locale {
83
+ let locale: Locale = locale.try_into()?;
84
+ let style: StyleGuide = style.try_into()?;
85
+ let opts: StyleOptions = opts.try_into()?;
86
+ Ok(match locale {
68
87
  Locale::EN => en::titlecase(chunk, style, opts),
69
88
  Locale::TR => tr::titlecase(chunk, style, opts),
70
- }
89
+ })
71
90
  }
72
91
 
73
92
  /// Convert a string to lower case following typesetting conventions for a target locale
74
- pub fn lowercase(chunk: impl Into<Chunk>, locale: impl Into<Locale>) -> String {
93
+ pub fn lowercase<TL>(chunk: impl Into<Chunk>, locale: TL) -> Result<String>
94
+ where
95
+ TL: TryInto<Locale>,
96
+ Error: From<TL::Error>,
97
+ {
75
98
  let chunk: Chunk = chunk.into();
76
- let locale: Locale = locale.into();
77
- match locale {
99
+ let locale: Locale = locale.try_into()?;
100
+ Ok(match locale {
78
101
  Locale::EN => en::lowercase(chunk),
79
102
  Locale::TR => tr::lowercase(chunk),
80
- }
103
+ })
81
104
  }
82
105
 
83
106
  /// Convert a string to upper case following typesetting conventions for a target locale
84
- pub fn uppercase(chunk: impl Into<Chunk>, locale: impl Into<Locale>) -> String {
107
+ pub fn uppercase<TL>(chunk: impl Into<Chunk>, locale: TL) -> Result<String>
108
+ where
109
+ TL: TryInto<Locale>,
110
+ Error: From<TL::Error>,
111
+ {
85
112
  let chunk: Chunk = chunk.into();
86
- let locale: Locale = locale.into();
87
- match locale {
113
+ let locale: Locale = locale.try_into()?;
114
+ Ok(match locale {
88
115
  Locale::EN => en::uppercase(chunk),
89
116
  Locale::TR => tr::uppercase(chunk),
90
- }
117
+ })
91
118
  }
92
119
 
93
120
  /// Convert a string to sentence case following typesetting conventions for a target locale
94
- pub fn sentencecase(chunk: impl Into<Chunk>, locale: impl Into<Locale>) -> String {
121
+ pub fn sentencecase<TL>(chunk: impl Into<Chunk>, locale: TL) -> Result<String>
122
+ where
123
+ TL: TryInto<Locale>,
124
+ Error: From<TL::Error>,
125
+ {
95
126
  let chunk: Chunk = chunk.into();
96
- let locale: Locale = locale.into();
97
- match locale {
127
+ let locale: Locale = locale.try_into()?;
128
+ Ok(match locale {
98
129
  Locale::EN => en::sentencecase(chunk),
99
130
  Locale::TR => tr::sentencecase(chunk),
100
- }
131
+ })
101
132
  }
102
133
 
103
134
  fn get_override<F>(word: &Word, overrides: &Option<Vec<Word>>, case_fn: F) -> Option<Word>
@@ -0,0 +1,190 @@
1
+ // SPDX-FileCopyrightText: © 2023 Caleb Maclennan <caleb@alerque.com>
2
+ // SPDX-License-Identifier: LGPL-3.0-only
3
+
4
+ use crate::*;
5
+ use mlua::prelude::*;
6
+
7
+ use crate::types::{Error, Result};
8
+
9
+ macro_rules! impl_into_luaresult {
10
+ ($($t:ty),*) => {
11
+ $(
12
+ impl Into<LuaResult<$t>> for $t {
13
+ fn into(self) -> LuaResult<$t> {
14
+ Ok(self)
15
+ }
16
+ }
17
+ )*
18
+ };
19
+ }
20
+
21
+ impl_into_luaresult!(Locale, Case, StyleGuide, StyleOptions);
22
+
23
+ impl From<Error> for LuaError {
24
+ fn from(err: Error) -> LuaError {
25
+ LuaError::RuntimeError(err.to_string())
26
+ }
27
+ }
28
+
29
+ impl TryFrom<LuaString> for Locale {
30
+ type Error = Error;
31
+ fn try_from(s: LuaString) -> Result<Self> {
32
+ s.to_string_lossy().try_into()
33
+ }
34
+ }
35
+
36
+ impl TryFrom<LuaString> for Case {
37
+ type Error = Error;
38
+ fn try_from(s: LuaString) -> Result<Self> {
39
+ s.to_string_lossy().try_into()
40
+ }
41
+ }
42
+
43
+ impl TryFrom<LuaString> for StyleGuide {
44
+ type Error = Error;
45
+ fn try_from(s: LuaString) -> Result<Self> {
46
+ s.to_string_lossy().try_into()
47
+ }
48
+ }
49
+
50
+ #[mlua::lua_module]
51
+ fn decasify(lua: &Lua) -> LuaResult<LuaTable> {
52
+ let exports = lua.create_table()?;
53
+ exports.set(
54
+ "case",
55
+ lua.create_function(
56
+ |_,
57
+ (chunk, case_, locale, styleguide, styleoptions): (
58
+ Chunk,
59
+ Case,
60
+ Locale,
61
+ StyleGuide,
62
+ StyleOptions,
63
+ )| { Ok(case(chunk, case_, locale, styleguide, styleoptions)?) },
64
+ )?,
65
+ )?;
66
+ exports.set(
67
+ "titlecase",
68
+ lua.create_function(
69
+ |_,
70
+ (chunk, locale, styleguide, styleoptions): (
71
+ Chunk,
72
+ Locale,
73
+ StyleGuide,
74
+ StyleOptions,
75
+ )| { Ok(titlecase(chunk, locale, styleguide, styleoptions)?) },
76
+ )?,
77
+ )?;
78
+ exports.set(
79
+ "lowercase",
80
+ lua.create_function(|_, (chunk, locale): (Chunk, Locale)| Ok(lowercase(chunk, locale)?))?,
81
+ )?;
82
+ exports.set(
83
+ "uppercase",
84
+ lua.create_function(|_, (chunk, locale): (Chunk, Locale)| Ok(uppercase(chunk, locale)?))?,
85
+ )?;
86
+ exports.set(
87
+ "sentencecase",
88
+ lua.create_function(|_, (chunk, locale): (Chunk, Locale)| {
89
+ Ok(sentencecase(chunk, locale)?)
90
+ })?,
91
+ )?;
92
+ let mt = lua.create_table()?;
93
+ let decasify = lua.create_function(
94
+ move |_,
95
+ (_, chunk, case_, locale, styleguide, styleoptions): (
96
+ LuaTable,
97
+ Chunk,
98
+ Case,
99
+ Locale,
100
+ StyleGuide,
101
+ Option<StyleOptions>,
102
+ )| {
103
+ Ok(case(
104
+ chunk,
105
+ case_,
106
+ locale,
107
+ styleguide,
108
+ styleoptions.unwrap_or_default(),
109
+ )?)
110
+ },
111
+ )?;
112
+ mt.set("__call", decasify)?;
113
+ exports.set_metatable(Some(mt))?;
114
+ let version = option_env!("VERGEN_GIT_DESCRIBE").unwrap_or_else(|| env!("CARGO_PKG_VERSION"));
115
+ let version = lua.create_string(version)?;
116
+ exports.set("version", version)?;
117
+ Ok(exports)
118
+ }
119
+
120
+ #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
121
+ impl FromLua for Chunk {
122
+ fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
123
+ let chunk = match value {
124
+ LuaValue::String(s) => s.to_string_lossy(),
125
+ _ => String::from(""),
126
+ }
127
+ .into();
128
+ Ok(chunk)
129
+ }
130
+ }
131
+
132
+ #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
133
+ impl FromLua for Locale {
134
+ fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
135
+ match value {
136
+ LuaValue::String(s) => s.try_into()?,
137
+ LuaValue::Nil => Self::default(),
138
+ _ => value.to_string().unwrap_or_default().try_into()?,
139
+ }
140
+ .into()
141
+ }
142
+ }
143
+
144
+ #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
145
+ impl FromLua for Case {
146
+ fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
147
+ match value {
148
+ LuaValue::String(s) => s.try_into()?,
149
+ LuaValue::Nil => Self::default(),
150
+ _ => value.to_string().unwrap_or_default().try_into()?,
151
+ }
152
+ .into()
153
+ }
154
+ }
155
+
156
+ #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
157
+ impl FromLua for StyleGuide {
158
+ fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
159
+ match value {
160
+ LuaValue::String(s) => s.try_into()?,
161
+ LuaValue::Nil => Self::default(),
162
+ _ => value.to_string().unwrap_or_default().try_into()?,
163
+ }
164
+ .into()
165
+ }
166
+ }
167
+
168
+ #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
169
+ impl FromLua for StyleOptions {
170
+ fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
171
+ match value {
172
+ LuaValue::Table(t) => {
173
+ let mut builder = StyleOptionsBuilder::new();
174
+ if let Ok(overrides_table) = t.get::<LuaTable>("overrides") {
175
+ let overrides: Vec<Word> = overrides_table
176
+ .sequence_values::<String>()
177
+ .collect::<LuaResult<Vec<_>>>()?
178
+ .into_iter()
179
+ .map(|s| s.into())
180
+ .collect();
181
+ builder = builder.overrides(overrides);
182
+ }
183
+ builder.build()
184
+ }
185
+ LuaValue::Nil => Self::default(),
186
+ _ => value.to_string().unwrap_or_default().try_into()?,
187
+ }
188
+ .into()
189
+ }
190
+ }
@@ -4,6 +4,12 @@
4
4
  use crate::types::*;
5
5
  use pyo3::prelude::*;
6
6
 
7
+ impl From<crate::types::Error> for PyErr {
8
+ fn from(err: crate::types::Error) -> Self {
9
+ pyo3::exceptions::PyValueError::new_err(err.to_string())
10
+ }
11
+ }
12
+
7
13
  #[pymodule]
8
14
  fn decasify(module: &Bound<'_, PyModule>) -> PyResult<()> {
9
15
  module.add_class::<Case>()?;
@@ -33,7 +39,7 @@ fn case(
33
39
  Some(words) => StyleOptionsBuilder::new().overrides(words).build(),
34
40
  None => StyleOptions::default(),
35
41
  };
36
- Ok(crate::case(&input, case, locale, style, opts))
42
+ Ok(crate::case(&input, case, locale, style, opts)?)
37
43
  }
38
44
 
39
45
  #[pyfunction]
@@ -48,23 +54,23 @@ fn titlecase(
48
54
  Some(words) => StyleOptionsBuilder::new().overrides(words).build(),
49
55
  None => StyleOptions::default(),
50
56
  };
51
- Ok(crate::titlecase(&input, locale, style, opts))
57
+ Ok(crate::titlecase(&input, locale, style, opts)?)
52
58
  }
53
59
 
54
60
  #[pyfunction]
55
61
  #[pyo3(signature = (input, locale))]
56
62
  fn lowercase(input: String, locale: Locale) -> PyResult<String> {
57
- Ok(crate::lowercase(&input, locale))
63
+ Ok(crate::lowercase(&input, locale)?)
58
64
  }
59
65
 
60
66
  #[pyfunction]
61
67
  #[pyo3(signature = (input, locale))]
62
68
  fn uppercase(input: String, locale: Locale) -> PyResult<String> {
63
- Ok(crate::uppercase(&input, locale))
69
+ Ok(crate::uppercase(&input, locale)?)
64
70
  }
65
71
 
66
72
  #[pyfunction]
67
73
  #[pyo3(signature = (input, locale))]
68
74
  fn sentencecase(input: String, locale: Locale) -> PyResult<String> {
69
- Ok(crate::sentencecase(&input, locale))
75
+ Ok(crate::sentencecase(&input, locale)?)
70
76
  }
@@ -1,11 +1,12 @@
1
1
  // SPDX-FileCopyrightText: © 2023 Caleb Maclennan <caleb@alerque.com>
2
2
  // SPDX-License-Identifier: LGPL-3.0-only
3
3
 
4
+ use snafu::prelude::*;
5
+ use std::convert::{Infallible, TryFrom};
6
+ use std::fmt::{Debug, Display, Formatter};
4
7
  use std::str::FromStr;
5
8
  use strum_macros::{Display, VariantNames};
6
9
 
7
- use snafu::prelude::*;
8
-
9
10
  #[cfg(feature = "pythonmodule")]
10
11
  use pyo3::prelude::*;
11
12
 
@@ -13,25 +14,32 @@ use pyo3::prelude::*;
13
14
  use wasm_bindgen::prelude::*;
14
15
 
15
16
  #[derive(Snafu)]
17
+ #[snafu(visibility(pub))]
16
18
  pub enum Error {
17
- #[snafu(display("Invalid input language {}", input))]
19
+ #[snafu(display("Invalid input language '{input}'"))]
18
20
  Locale { input: String },
19
21
 
20
- #[snafu(display("Invalid target case {}", input))]
22
+ #[snafu(display("Invalid target case '{input}'"))]
21
23
  Case { input: String },
22
24
 
23
- #[snafu(display("Invalid preferred style guide {}", input))]
25
+ #[snafu(display("Invalid preferred style guide '{input}'"))]
24
26
  StyleGuide { input: String },
25
27
 
26
- #[snafu(display("Invalid style options {}", input))]
28
+ #[snafu(display("Invalid style options '{input}'"))]
27
29
  StyleOptions { input: String },
28
30
  }
29
31
 
30
32
  // Clap CLI errors are reported using the Debug trait, but Snafu sets up the Display trait.
31
33
  // So we delegate. c.f. https://github.com/shepmaster/snafu/issues/110
32
- impl std::fmt::Debug for Error {
33
- fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
34
- std::fmt::Display::fmt(self, fmt)
34
+ impl Debug for Error {
35
+ fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
36
+ Display::fmt(self, fmt)
37
+ }
38
+ }
39
+
40
+ impl From<Infallible> for Error {
41
+ fn from(_: Infallible) -> Self {
42
+ unreachable!()
35
43
  }
36
44
  }
37
45
 
@@ -99,12 +107,6 @@ pub struct StyleOptions {
99
107
  pub overrides: Option<Vec<Word>>,
100
108
  }
101
109
 
102
- impl From<&str> for StyleOptions {
103
- fn from(s: &str) -> Self {
104
- Self::from_str(s).unwrap()
105
- }
106
- }
107
-
108
110
  impl FromStr for StyleOptions {
109
111
  type Err = Error;
110
112
  fn from_str(s: &str) -> Result<Self> {
@@ -115,6 +117,35 @@ impl FromStr for StyleOptions {
115
117
  }
116
118
  }
117
119
 
120
+ impl TryFrom<&str> for StyleOptions {
121
+ type Error = Error;
122
+ fn try_from(s: &str) -> Result<Self> {
123
+ Self::from_str(s)
124
+ }
125
+ }
126
+
127
+ impl TryFrom<String> for StyleOptions {
128
+ type Error = Error;
129
+ fn try_from(s: String) -> Result<Self> {
130
+ Self::from_str(&s)
131
+ }
132
+ }
133
+
134
+ impl TryFrom<&String> for StyleOptions {
135
+ type Error = Error;
136
+ fn try_from(s: &String) -> Result<Self> {
137
+ Self::from_str(s)
138
+ }
139
+ }
140
+
141
+ impl TryFrom<&[u8]> for StyleOptions {
142
+ type Error = Error;
143
+ fn try_from(s: &[u8]) -> Result<Self> {
144
+ let s = String::from_utf8_lossy(s);
145
+ Self::from_str(&s)
146
+ }
147
+ }
148
+
118
149
  #[derive(Debug)]
119
150
  pub struct StyleOptionsBuilder {
120
151
  overrides: Option<Vec<Word>>,
@@ -155,28 +186,33 @@ impl FromStr for Locale {
155
186
  }
156
187
  }
157
188
 
158
- impl From<&str> for Locale {
159
- fn from(s: &str) -> Self {
160
- Self::from_str(s).unwrap()
189
+ impl TryFrom<&str> for Locale {
190
+ type Error = Error;
191
+ fn try_from(s: &str) -> Result<Self> {
192
+ Self::from_str(s)
161
193
  }
162
194
  }
163
195
 
164
- impl From<String> for Locale {
165
- fn from(s: String) -> Self {
166
- Self::from_str(s.as_ref()).unwrap()
196
+ impl TryFrom<String> for Locale {
197
+ type Error = Error;
198
+ fn try_from(s: String) -> Result<Self> {
199
+ Self::from_str(&s)
167
200
  }
168
201
  }
169
202
 
170
- impl From<&String> for Locale {
171
- fn from(s: &String) -> Self {
172
- Self::from_str(s.as_ref()).unwrap()
203
+ impl TryFrom<&String> for Locale {
204
+ type Error = Error;
205
+ fn try_from(s: &String) -> Result<Self> {
206
+ Self::from_str(s)
173
207
  }
174
208
  }
175
209
 
176
- impl From<&[u8]> for Locale {
177
- fn from(s: &[u8]) -> Self {
178
- let s = String::from_utf8(s.to_vec()).unwrap();
179
- Self::from_str(s.as_ref()).unwrap()
210
+ impl TryFrom<&[u8]> for Locale {
211
+ type Error = Error;
212
+
213
+ fn try_from(s: &[u8]) -> Result<Self> {
214
+ let s = String::from_utf8_lossy(s);
215
+ Self::from_str(&s)
180
216
  }
181
217
  }
182
218
 
@@ -193,28 +229,33 @@ impl FromStr for Case {
193
229
  }
194
230
  }
195
231
 
196
- impl From<&str> for Case {
197
- fn from(s: &str) -> Self {
198
- Self::from_str(s).unwrap()
232
+ impl TryFrom<&str> for Case {
233
+ type Error = Error;
234
+ fn try_from(s: &str) -> Result<Self> {
235
+ Self::from_str(s)
199
236
  }
200
237
  }
201
238
 
202
- impl From<String> for Case {
203
- fn from(s: String) -> Self {
204
- Self::from_str(s.as_ref()).unwrap()
239
+ impl TryFrom<String> for Case {
240
+ type Error = Error;
241
+ fn try_from(s: String) -> Result<Self> {
242
+ Self::from_str(&s)
205
243
  }
206
244
  }
207
245
 
208
- impl From<&String> for Case {
209
- fn from(s: &String) -> Self {
210
- Self::from_str(s.as_ref()).unwrap()
246
+ impl TryFrom<&String> for Case {
247
+ type Error = Error;
248
+ fn try_from(s: &String) -> Result<Self> {
249
+ Self::from_str(s)
211
250
  }
212
251
  }
213
252
 
214
- impl From<&[u8]> for Case {
215
- fn from(s: &[u8]) -> Self {
216
- let s = String::from_utf8(s.to_vec()).unwrap();
217
- Self::from_str(s.as_ref()).unwrap()
253
+ impl TryFrom<&[u8]> for Case {
254
+ type Error = Error;
255
+
256
+ fn try_from(s: &[u8]) -> Result<Self> {
257
+ let s = String::from_utf8_lossy(s);
258
+ Self::from_str(&s)
218
259
  }
219
260
  }
220
261
 
@@ -234,28 +275,24 @@ impl FromStr for StyleGuide {
234
275
  }
235
276
  }
236
277
 
237
- impl From<&str> for StyleGuide {
238
- fn from(s: &str) -> Self {
239
- Self::from_str(s).unwrap()
278
+ impl TryFrom<&str> for StyleGuide {
279
+ type Error = Error;
280
+ fn try_from(s: &str) -> Result<Self> {
281
+ Self::from_str(s)
240
282
  }
241
283
  }
242
284
 
243
- impl From<String> for StyleGuide {
244
- fn from(s: String) -> Self {
245
- Self::from_str(s.as_ref()).unwrap()
285
+ impl TryFrom<String> for StyleGuide {
286
+ type Error = Error;
287
+ fn try_from(s: String) -> Result<Self> {
288
+ Self::from_str(&s)
246
289
  }
247
290
  }
248
291
 
249
- impl From<&String> for StyleGuide {
250
- fn from(s: &String) -> Self {
251
- Self::from_str(s.as_ref()).unwrap()
252
- }
253
- }
254
-
255
- impl From<&[u8]> for StyleGuide {
256
- fn from(s: &[u8]) -> Self {
257
- let s = String::from_utf8(s.to_vec()).unwrap();
258
- Self::from_str(s.as_ref()).unwrap()
292
+ impl TryFrom<&String> for StyleGuide {
293
+ type Error = Error;
294
+ fn try_from(s: &String) -> Result<Self> {
295
+ Self::from_str(s)
259
296
  }
260
297
  }
261
298
 
@@ -267,3 +304,12 @@ impl From<Option<StyleGuide>> for StyleGuide {
267
304
  }
268
305
  }
269
306
  }
307
+
308
+ impl TryFrom<&[u8]> for StyleGuide {
309
+ type Error = Error;
310
+
311
+ fn try_from(s: &[u8]) -> Result<Self> {
312
+ let s = String::from_utf8_lossy(s);
313
+ Self::from_str(&s)
314
+ }
315
+ }
@@ -15,7 +15,7 @@ pub fn case(
15
15
  opts: Option<StyleOptions>,
16
16
  ) -> Result<String, JsError> {
17
17
  let opts = opts.unwrap_or_default();
18
- Ok(crate::case(input, case, locale, style, opts))
18
+ Ok(crate::case(input, case, locale, style, opts)?)
19
19
  }
20
20
 
21
21
  #[wasm_bindgen]
@@ -26,20 +26,20 @@ pub fn titlecase(
26
26
  opts: Option<StyleOptions>,
27
27
  ) -> Result<String, JsError> {
28
28
  let opts = opts.unwrap_or_default();
29
- Ok(crate::titlecase(input, locale, style, opts))
29
+ Ok(crate::titlecase(input, locale, style, opts)?)
30
30
  }
31
31
 
32
32
  #[wasm_bindgen]
33
33
  pub fn lowercase(input: &str, locale: Locale) -> Result<String, JsError> {
34
- Ok(crate::lowercase(input, locale))
34
+ Ok(crate::lowercase(input, locale)?)
35
35
  }
36
36
 
37
37
  #[wasm_bindgen]
38
38
  pub fn uppercase(input: &str, locale: Locale) -> Result<String, JsError> {
39
- Ok(crate::uppercase(input, locale))
39
+ Ok(crate::uppercase(input, locale)?)
40
40
  }
41
41
 
42
42
  #[wasm_bindgen]
43
43
  pub fn sentencecase(input: &str, locale: Locale) -> Result<String, JsError> {
44
- Ok(crate::sentencecase(input, locale))
44
+ Ok(crate::sentencecase(input, locale)?)
45
45
  }
@@ -5,11 +5,11 @@ use decasify::*;
5
5
 
6
6
  #[test]
7
7
  fn cast_from_str() {
8
- let res = titlecase("FIST", "en", "gruber", "default");
8
+ let res = titlecase("FIST", "en", "gruber", "default").unwrap();
9
9
  assert_eq!(res, "Fist");
10
- let res = titlecase("FIST", "tr", "", "default");
10
+ let res = titlecase("FIST", "tr", "", "default").unwrap();
11
11
  assert_eq!(res, "Fıst");
12
- let res = titlecase("FIST", "tr", "default", "default");
12
+ let res = titlecase("FIST", "tr", "default", "default").unwrap();
13
13
  assert_eq!(res, "Fıst");
14
14
  }
15
15
 
@@ -20,23 +20,24 @@ fn cast_from_legacy_option() {
20
20
  "en",
21
21
  Some(StyleGuide::DaringFireball),
22
22
  StyleOptions::default(),
23
- );
23
+ )
24
+ .unwrap();
24
25
  assert_eq!(res, "Fist");
25
- let res = titlecase("FIST", "en", None, StyleOptions::default());
26
+ let res = titlecase("FIST", "en", None, StyleOptions::default()).unwrap();
26
27
  assert_eq!(res, "Fist");
27
28
  }
28
29
 
29
30
  #[test]
30
31
  fn custom_style_guide() {
31
32
  let options: StyleOptions = StyleOptionsBuilder::new().overrides(vec!["fOO"]).build();
32
- let res = titlecase("foo bar", "tr", StyleGuide::LanguageDefault, options);
33
+ let res = titlecase("foo bar", "tr", StyleGuide::LanguageDefault, options).unwrap();
33
34
  assert_eq!(res, "fOO Bar");
34
35
  }
35
36
 
36
37
  #[cfg(feature = "unstable-trait")]
37
38
  #[test]
38
39
  fn trait_chery() {
39
- use decasify::Decasify;
40
+ use Decasify::Decasify;
40
41
  let s = "WHY THE LONG FACE?";
41
42
  assert_eq!(s.to_case("sentence", "en", None), "Why the long face?");
42
43
  assert_eq!(
@@ -53,7 +54,7 @@ macro_rules! case {
53
54
  ($name:ident, $case:expr, $locale:expr, $style:expr, $opts:expr, $input:expr, $expected:expr) => {
54
55
  #[test]
55
56
  fn $name() {
56
- let actual = case($input, $case, $locale, $style, $opts);
57
+ let actual = case($input, $case, $locale, $style, $opts).unwrap();
57
58
  assert_eq!(actual, $expected);
58
59
  }
59
60
  };
@@ -103,7 +104,7 @@ macro_rules! titlecase {
103
104
  ($name:ident, $locale:expr, $style:expr, $opts:expr, $input:expr, $expected:expr) => {
104
105
  #[test]
105
106
  fn $name() {
106
- let actual = titlecase($input, $locale, $style, $opts);
107
+ let actual = titlecase($input, $locale, $style, $opts).unwrap();
107
108
  assert_eq!(actual, $expected);
108
109
  }
109
110
  };
@@ -247,7 +248,7 @@ macro_rules! lowercase {
247
248
  ($name:ident, $locale:expr, $input:expr, $expected:expr) => {
248
249
  #[test]
249
250
  fn $name() {
250
- let actual = lowercase($input, $locale);
251
+ let actual = lowercase($input, $locale).unwrap();
251
252
  assert_eq!(actual, $expected);
252
253
  }
253
254
  };
@@ -266,7 +267,7 @@ macro_rules! uppercase {
266
267
  ($name:ident, $locale:expr, $input:expr, $expected:expr) => {
267
268
  #[test]
268
269
  fn $name() {
269
- let actual = uppercase($input, $locale);
270
+ let actual = uppercase($input, $locale).unwrap();
270
271
  assert_eq!(actual, $expected);
271
272
  }
272
273
  };
@@ -285,7 +286,7 @@ macro_rules! sentencecase {
285
286
  ($name:ident, $locale:expr, $input:expr, $expected:expr) => {
286
287
  #[test]
287
288
  fn $name() {
288
- let actual = sentencecase($input, $locale);
289
+ let actual = sentencecase($input, $locale).unwrap();
289
290
  assert_eq!(actual, $expected);
290
291
  }
291
292
  };
@@ -1,122 +0,0 @@
1
- // SPDX-FileCopyrightText: © 2023 Caleb Maclennan <caleb@alerque.com>
2
- // SPDX-License-Identifier: LGPL-3.0-only
3
-
4
- use crate::*;
5
- use mlua::prelude::*;
6
-
7
- #[mlua::lua_module]
8
- fn decasify(lua: &Lua) -> LuaResult<LuaTable> {
9
- let exports = lua.create_table()?;
10
- exports.set(
11
- "case",
12
- LuaFunction::wrap_raw::<_, (Chunk, Case, Locale, StyleGuide, StyleOptions)>(case),
13
- )?;
14
- exports.set(
15
- "titlecase",
16
- LuaFunction::wrap_raw::<_, (Chunk, Locale, StyleGuide, StyleOptions)>(titlecase),
17
- )?;
18
- exports.set(
19
- "lowercase",
20
- LuaFunction::wrap_raw::<_, (Chunk, Locale)>(lowercase),
21
- )?;
22
- exports.set(
23
- "uppercase",
24
- LuaFunction::wrap_raw::<_, (Chunk, Locale)>(uppercase),
25
- )?;
26
- exports.set(
27
- "sentencecase",
28
- LuaFunction::wrap_raw::<_, (Chunk, Locale)>(sentencecase),
29
- )?;
30
- let mt = lua.create_table()?;
31
- let decasify = lua.create_function(
32
- move |_,
33
- (_, chunk, case_, locale, styleguide, opts): (
34
- LuaTable,
35
- Chunk,
36
- Case,
37
- Locale,
38
- StyleGuide,
39
- Option<StyleOptions>,
40
- )| {
41
- Ok(case(
42
- chunk,
43
- case_,
44
- locale,
45
- styleguide,
46
- opts.unwrap_or_default(),
47
- ))
48
- },
49
- )?;
50
- mt.set("__call", decasify)?;
51
- exports.set_metatable(Some(mt))?;
52
- let version = option_env!("VERGEN_GIT_DESCRIBE").unwrap_or_else(|| env!("CARGO_PKG_VERSION"));
53
- let version = lua.create_string(version)?;
54
- exports.set("version", version)?;
55
- Ok(exports)
56
- }
57
-
58
- #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
59
- impl FromLua for Chunk {
60
- fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
61
- match value {
62
- LuaValue::String(s) => Ok(s.to_string_lossy().into()),
63
- _ => Ok("".into()),
64
- }
65
- }
66
- }
67
-
68
- #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
69
- impl FromLua for Locale {
70
- fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
71
- match value {
72
- LuaValue::String(s) => Ok(s.to_string_lossy().into()),
73
- LuaValue::Nil => Ok(Self::default()),
74
- _ => unimplemented!(),
75
- }
76
- }
77
- }
78
-
79
- #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
80
- impl FromLua for Case {
81
- fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
82
- match value {
83
- LuaValue::String(s) => Ok(s.to_string_lossy().into()),
84
- LuaValue::Nil => Ok(Self::default()),
85
- _ => unimplemented!(),
86
- }
87
- }
88
- }
89
-
90
- #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
91
- impl FromLua for StyleGuide {
92
- fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
93
- match value {
94
- LuaValue::String(s) => Ok(s.to_string_lossy().into()),
95
- LuaValue::Nil => Ok(Self::default()),
96
- _ => unimplemented!(),
97
- }
98
- }
99
- }
100
-
101
- #[cfg_attr(docsrs, doc(cfg(feature = "luamodule")))]
102
- impl FromLua for StyleOptions {
103
- fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
104
- match value {
105
- LuaValue::Table(t) => {
106
- let mut builder = StyleOptionsBuilder::new();
107
- if let Ok(overrides_table) = t.get::<LuaTable>("overrides") {
108
- let overrides: Vec<Word> = overrides_table
109
- .sequence_values::<String>()
110
- .collect::<LuaResult<Vec<_>>>()?
111
- .into_iter()
112
- .map(|s| s.into())
113
- .collect();
114
- builder = builder.overrides(overrides);
115
- }
116
- Ok(builder.build())
117
- }
118
- LuaValue::Nil => Ok(Self::default()),
119
- _ => unimplemented!(),
120
- }
121
- }
122
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes