diff --git a/Cargo.lock b/Cargo.lock index cb42bc4..c6c2c1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -383,6 +383,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "errno" version = "0.3.9" @@ -1103,23 +1109,25 @@ dependencies = [ [[package]] name = "mlua" -version = "0.9.9" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7" +checksum = "0f6ddbd668297c46be4bdea6c599dcc1f001a129586272d53170b7ac0a62961e" dependencies = [ + "anyhow", "bstr", + "either", "mlua-sys", "mlua_derive", "num-traits", - "once_cell", + "parking_lot", "rustc-hash", ] [[package]] name = "mlua-sys" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe026d6bd1583a9cf9080e189030ddaea7e6f5f0deb366a8e26f8a26c4135b8" +checksum = "e9eebac25c35a13285456c88ee2fde93d9aee8bcfdaf03f9d6d12be3391351ec" dependencies = [ "cc", "cfg-if", @@ -1128,9 +1136,9 @@ dependencies = [ [[package]] name = "mlua_derive" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09697a6cec88e7f58a02c7ab5c18c611c6907c8654613df9cc0192658a4fb859" +checksum = "2cfc5faa2e0d044b3f5f0879be2920e0a711c97744c42cf1c295cb183668933e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 55cd855..7308be3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,15 +49,18 @@ strum = "0.26" strum_macros = "0.26" unicode_titlecase = "2.3" + [dependencies.anyhow] + version = "1.0" + [dependencies.clap] version = "4.5" optional = true features = ["derive", "wrap_help"] [dependencies.mlua] - version = "0.9" + version = "0.10.0" optional = true - features = ["module"] + features = ["module", "anyhow"] [dependencies.pyo3] version = "0.22" diff --git a/src/content.rs b/src/content.rs index 653b575..e652414 100644 --- a/src/content.rs +++ b/src/content.rs @@ -3,7 +3,7 @@ use crate::types::Result; use regex::Regex; -use std::{borrow::Cow, error, fmt, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt, fmt::Display, str::FromStr}; #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] @@ -12,7 +12,7 @@ pub enum Segment { Word(String), } -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] #[non_exhaustive] pub struct Chunk { pub segments: Vec, @@ -56,7 +56,7 @@ impl From<&Cow<'_, str>> for Chunk { } impl FromStr for Chunk { - type Err = Box; + type Err = anyhow::Error; fn from_str(s: &str) -> Result { Ok(split_chunk(s)) } diff --git a/src/lua.rs b/src/lua.rs index 5707c26..21dc4c7 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -8,102 +8,68 @@ pub use crate::types::{Case, Locale, Result, StyleGuide}; #[mlua::lua_module] fn decasify(lua: &Lua) -> LuaResult { - let exports = lua.create_table().unwrap(); - let case = lua.create_function(case)?; - exports.set("case", case).unwrap(); - let titlecase = lua.create_function(titlecase)?; - exports.set("titlecase", titlecase).unwrap(); - let lowercase = lua.create_function(lowercase)?; - exports.set("lowercase", lowercase).unwrap(); - let uppercase = lua.create_function(uppercase)?; - exports.set("uppercase", uppercase).unwrap(); - let sentencecase = lua.create_function(sentencecase)?; - exports.set("sentencecase", sentencecase).unwrap(); + let exports = lua.create_table()?; + exports.set( + "case", + LuaFunction::wrap_raw::<_, (Chunk, Case, Locale, StyleGuide)>(to_case), + )?; + exports.set( + "titlecase", + LuaFunction::wrap_raw::<_, (Chunk, Locale, StyleGuide)>(to_titlecase), + )?; + exports.set( + "lowercase", + LuaFunction::wrap_raw::<_, (Chunk, Locale)>(to_lowercase), + )?; + exports.set( + "uppercase", + LuaFunction::wrap_raw::<_, (Chunk, Locale)>(to_uppercase), + )?; + exports.set( + "sentencecase", + LuaFunction::wrap_raw::<_, (Chunk, Locale)>(to_sentencecase), + )?; let version = option_env!("VERGEN_GIT_DESCRIBE").unwrap_or_else(|| env!("CARGO_PKG_VERSION")); let version = lua.create_string(version)?; - exports.set("version", version).unwrap(); + exports.set("version", version)?; Ok(exports) } -fn case<'a>( - lua: &'a Lua, - (input, case, locale, style): (LuaString<'a>, LuaValue<'a>, LuaValue<'a>, LuaValue<'a>), -) -> LuaResult> { - let input = input.to_string_lossy(); - let case: Case = match case { - LuaValue::String(s) => s.to_string_lossy().parse().unwrap_or(Case::Title), - _ => Case::Title, - }; - let locale: Locale = match locale { - LuaValue::String(s) => s.to_string_lossy().parse().unwrap_or(Locale::EN), - _ => Locale::EN, - }; - let style: StyleGuide = match style { - LuaValue::String(s) => s - .to_string_lossy() - .parse() - .unwrap_or(StyleGuide::LanguageDefault), - _ => StyleGuide::LanguageDefault, - }; - let output = to_case(&input, case, locale, style); - lua.create_string(output) +impl FromLua for Chunk { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult { + match value { + LuaValue::String(s) => Ok(s.to_string_lossy().into()), + _ => Ok("".into()), + } + } } -fn titlecase<'a>( - lua: &'a Lua, - (input, locale, style): (LuaString<'a>, LuaValue<'a>, LuaValue<'a>), -) -> LuaResult> { - let input = input.to_string_lossy(); - let locale: Locale = match locale { - LuaValue::String(s) => s.to_string_lossy().parse().unwrap_or(Locale::EN), - _ => Locale::EN, - }; - let style: StyleGuide = match style { - LuaValue::String(s) => s - .to_string_lossy() - .parse() - .unwrap_or(StyleGuide::LanguageDefault), - _ => StyleGuide::LanguageDefault, - }; - let output = to_titlecase(&input, locale, style); - lua.create_string(output) +impl FromLua for Locale { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult { + match value { + LuaValue::String(s) => Ok(s.to_string_lossy().into()), + LuaValue::Nil => Ok(Self::default()), + _ => unimplemented!(), + } + } } -fn lowercase<'a>( - lua: &'a Lua, - (input, locale): (LuaString<'a>, LuaValue<'a>), -) -> LuaResult> { - let input = input.to_string_lossy(); - let locale: Locale = match locale { - LuaValue::String(s) => s.to_string_lossy().parse().unwrap_or(Locale::EN), - _ => Locale::EN, - }; - let output = to_lowercase(&input, locale); - lua.create_string(output) +impl FromLua for Case { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult { + match value { + LuaValue::String(s) => Ok(s.to_string_lossy().into()), + LuaValue::Nil => Ok(Self::default()), + _ => unimplemented!(), + } + } } -fn uppercase<'a>( - lua: &'a Lua, - (input, locale): (LuaString<'a>, LuaValue<'a>), -) -> LuaResult> { - let input = input.to_string_lossy(); - let locale: Locale = match locale { - LuaValue::String(s) => s.to_string_lossy().parse().unwrap_or(Locale::EN), - _ => Locale::EN, - }; - let output = to_uppercase(&input, locale); - lua.create_string(output) -} - -fn sentencecase<'a>( - lua: &'a Lua, - (input, locale): (LuaString<'a>, LuaValue<'a>), -) -> LuaResult> { - let input = input.to_string_lossy(); - let locale: Locale = match locale { - LuaValue::String(s) => s.to_string_lossy().parse().unwrap_or(Locale::EN), - _ => Locale::EN, - }; - let output = to_sentencecase(&input, locale); - lua.create_string(output) +impl FromLua for StyleGuide { + fn from_lua(value: LuaValue, _: &Lua) -> LuaResult { + match value { + LuaValue::String(s) => Ok(s.to_string_lossy().into()), + LuaValue::Nil => Ok(Self::default()), + _ => unimplemented!(), + } + } } diff --git a/src/types.rs b/src/types.rs index c819a90..a20ba5e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: © 2023 Caleb Maclennan // SPDX-License-Identifier: LGPL-3.0-only -use std::{error, fmt, fmt::Display, result, str::FromStr}; +use std::{error, fmt, fmt::Display, str::FromStr}; use strum_macros::{Display, VariantNames}; #[cfg(feature = "pythonmodule")] @@ -10,7 +10,7 @@ use pyo3::prelude::*; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::*; -pub type Result = result::Result>; +pub type Result = anyhow::Result; #[derive(Debug)] pub struct Error(pub String); @@ -68,12 +68,12 @@ pub enum StyleGuide { } impl FromStr for Locale { - type Err = Box; - fn from_str(s: &str) -> Result { + type Err = anyhow::Error; + fn from_str(s: &str) -> crate::Result { match s.to_ascii_lowercase().as_str() { "en" | "English" | "en_en" => Ok(Locale::EN), "tr" | "Turkish" | "tr_tr" | "türkçe" => Ok(Locale::TR), - _ => Err(Box::new(Error("Invalid input language".into()))), + _ => Err(anyhow::Error::new(Error("Invalid input language".into()))), } } } @@ -97,14 +97,14 @@ impl From<&String> for Locale { } impl FromStr for Case { - type Err = Box; - fn from_str(s: &str) -> Result { + type Err = anyhow::Error; + fn from_str(s: &str) -> crate::Result { match s.to_ascii_lowercase().as_str().trim_end_matches("case") { "lower" => Ok(Case::Lower), "sentence" => Ok(Case::Sentence), "title" => Ok(Case::Title), "upper" => Ok(Case::Upper), - _ => Err(Box::new(Error("Unknown target case".into()))), + _ => Err(anyhow::Error::new(Error("Unknown target case".into()))), } } } @@ -128,8 +128,8 @@ impl From<&String> for Case { } impl FromStr for StyleGuide { - type Err = Box; - fn from_str(s: &str) -> Result { + type Err = anyhow::Error; + fn from_str(s: &str) -> crate::Result { match s.to_ascii_lowercase().as_str() { "daringfireball" | "gruber" | "fireball" => Ok(StyleGuide::DaringFireball), "associatedpress" | "ap" => Ok(StyleGuide::AssociatedPress), @@ -137,7 +137,7 @@ impl FromStr for StyleGuide { "default" | "languagedefault" | "language" | "none" | "" => { Ok(StyleGuide::LanguageDefault) } - _ => Err(Box::new(Error("Invalid style guide".into()))), + _ => Err(anyhow::Error::new(Error("Invalid style guide".into()))), } } }