Skip to content

Commit f7dc641

Browse files
committed
Refactor native extension
- Add `Transform` and `TryTransform` traits - Move Rust functions outside `methods!` macro - Change naming to match conventions
1 parent 8f017c6 commit f7dc641

File tree

1 file changed

+98
-54
lines changed

1 file changed

+98
-54
lines changed

ext/case_transform/src/lib.rs

+98-54
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,124 @@
22
extern crate ruru;
33
extern crate inflector;
44

5-
// // dash: kebab-case
6-
use inflector::cases::kebabcase::to_kebab_case;
7-
// // underscore: snake_case
8-
use inflector::cases::snakecase::to_snake_case;
9-
// // camel_lower: camelCase
10-
use inflector::cases::camelcase::to_camel_case;
11-
// // camel: ClassCase (PascalCase)
12-
use inflector::cases::classcase::to_class_case;
13-
14-
use ruru::{Class, Object, RString, Hash, Array, Symbol, AnyObject, VM};
15-
use ruru::types::ValueType;
5+
use inflector::cases::{camelcase, classcase, kebabcase, snakecase};
166

17-
class!(CaseTransform);
7+
use ruru::{Class, Object, VerifiedObject, RString, Hash, Array, Symbol, AnyObject};
8+
use ruru::types::ValueType;
9+
use ruru::result::Error as RuruError;
1810

19-
methods! (
20-
CaseTransform,
21-
itself,
11+
trait Transform: Object {
12+
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject;
13+
}
2214

23-
fn deepTransformKeys(hash: Hash, block: &Fn(String) -> String) -> Hash {
24-
let result = Hash::new();
15+
impl Transform for AnyObject {
16+
fn transform(&self, _transform_function: &Fn(String) -> String) -> AnyObject {
17+
self.clone()
18+
}
19+
}
2520

26-
hash.unwrap().each(|key, value| {
27-
let newValue = if value.ty() == ValueType::Hash { deepTransformKeys(value, block).to_any_object() } else { value };
28-
let newKey = RString::new(block(key.unwrap().to_string()));
29-
result.store(newKey, newValue);
30-
});
21+
impl Transform for RString {
22+
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject {
23+
let result = transform_function(self.to_string();
3124

32-
result
25+
RString::new(&result)).to_any_object()
3326
}
27+
}
28+
29+
impl Transform for Symbol {
30+
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject {
31+
let result = transform_function(self.to_string());
3432

35-
fn transformArray(value: Array, transformMethod: &Fn(AnyObject) -> AnyObject) -> Array {
36-
value.map(|item| transformMethod(item)).unwrap()
33+
Symbol::new(&result).to_any_object()
3734
}
35+
}
36+
37+
impl Transform for Hash {
38+
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject {
39+
let mut result = Hash::new();
40+
41+
self.each(|key, value| {
42+
let new_key = transform(key, transform_function);
43+
let new_value = match value.ty() {
44+
ValueType::Hash => transform(value, transform_function),
45+
_ => value,
46+
};
3847

39-
fn transformHash(value: Hash, transformMethod: &Fn(AnyObject) -> AnyObject) -> Hash {
40-
deepTransformKeys(value, |key| transformMethod(key))
48+
result.store(new_key, new_value);
49+
});
50+
51+
result.to_any_object()
4152
}
53+
}
54+
55+
impl Transform for Array {
56+
fn transform(&self, transform_function: &Fn(String) -> String) -> AnyObject {
57+
// Temp hack to consume &self for iterator
58+
let result = unsafe { self.to_any_object().to::<Array>() };
4259

43-
fn transformSymbol(value: Symbol, transformMethod: &Fn(AnyObject) -> AnyObject) -> Symbol {
44-
let transformed = transformMethod(value);
45-
Symbol::new(transformed);
60+
result.into_iter()
61+
.map(|item| transform(item, transform_function))
62+
.collect::<Array>()
63+
.to_any_object()
4664
}
65+
}
4766

48-
fn transform(
49-
value: AnyObject,
50-
objectTransform: &Fn(AnyObject) -> AnyObject,
51-
keyTransform: &Fn(String) -> String
52-
) -> AnyObject {
53-
match value.unwrap().ty() {
54-
ValueType::Array => transformArray(value, objectTransform).to_any_object(),
55-
ValueType::Hash => transformHash(value, objectTransform).to_any_object(),
56-
ValueType::Symbol => transformSymbol(value, objectTransform).to_any_object(),
57-
ValueType::RString => keyTransform(value).to_any_object(),
58-
ValueType::Object => value
59-
}
67+
trait TryTransform: Object {
68+
fn try_transform<T>(&self,
69+
transform_function: &Fn(String) -> String)
70+
-> Result<AnyObject, RuruError>
71+
where T: VerifiedObject + Transform
72+
{
73+
self.try_convert_to::<T>().map(|object| object.transform(transform_function))
6074
}
75+
}
76+
77+
impl TryTransform for AnyObject {}
78+
79+
fn transform(object: AnyObject, key_transform: &Fn(String) -> String) -> AnyObject {
80+
let result = object.try_transform::<RString>(key_transform)
81+
.or_else(|_| object.try_transform::<Symbol>(key_transform))
82+
.or_else(|_| object.try_transform::<Array>(key_transform))
83+
.or_else(|_| object.try_transform::<Hash>(key_transform))
84+
.or_else(|_| object.try_transform::<AnyObject>(key_transform));
85+
86+
result.unwrap()
87+
}
88+
89+
fn to_pascal_case(key: String) -> String {
90+
classcase::to_class_case(snakecase::to_snake_case(key))
91+
}
92+
93+
fn to_camel_case(key: String) -> String {
94+
camelcase::to_camel_case(snakecase::to_snake_case(key))
95+
}
96+
97+
fn to_dashed_case(key: String) -> String {
98+
kebabcase::to_kebab_case(snakecase::to_snake_case(key))
99+
}
100+
101+
fn to_snake_case(key: String) -> String {
102+
snakecase::to_snake_case(key)
103+
}
61104

62-
fn toPascalCase(key: String) -> String { to_class_case(to_snake_case(key.unwrap())) }
63-
fn toCamelCase(key: String) -> String { to_camel_case(to_snake_case(key.unwrap())) }
64-
fn toDashedCase(key: String) -> String { to_kebab_case(to_snake_case(key.unwrap())) }
65-
fn toSnakeCase(key: String) -> String { to_snake_case(key.unwrap()) }
105+
class!(CaseTransform);
106+
107+
methods! (
108+
CaseTransform,
109+
_itself,
66110

67-
fn camel(value: AnyObject) -> AnyObject { transform(value.unwrap().to_any_object(), &camel, &toPascalCase) }
68-
fn camelLower(value: AnyObject) -> AnyObject { transform(value.unwrap().to_any_object(), &camelLower, &toCamelCase) }
69-
fn dash(value: AnyObject) -> AnyObject { transform(value.unwrap().to_any_object(), &dash, &toDashedCase) }
70-
fn underscore(value: AnyObject) -> AnyObject { transform(value.unwrap(), &underscore, &toSnakeCase) }
71-
fn unaltered(value: AnyObject) -> AnyObject { value.unwrap().to_any_object() }
111+
fn camel(object: AnyObject) -> AnyObject { transform(value.unwrap(), &to_pascal_case) }
112+
fn camel_lower(object: AnyObject) -> AnyObject { transform(object.unwrap(), &to_camel_case) }
113+
fn dash(object: AnyObject) -> AnyObject { transform(object.unwrap(), &to_dashed_case) }
114+
fn underscore(object: AnyObject) -> AnyObject { transform(object.unwrap(), &to_snake_case) }
115+
fn unaltered(object: AnyObject) -> AnyObject { object.unwrap() }
72116
);
73117

74118
#[no_mangle]
75-
pub extern fn initialize_case_transform() {
76-
Class::new("CaseTransform", None).define(|itself| {
119+
pub extern "C" fn initialize_case_transform() {
120+
Class::from_existing("CaseTransform").define(|itself| {
77121
itself.def_self("camel", camel);
78-
itself.def_self("camel_lower", camelLower);
122+
itself.def_self("camel_lower", camel_lower);
79123
itself.def_self("dash", dash);
80124
itself.def_self("underscore", underscore);
81125
itself.def_self("unaltered", unaltered);

0 commit comments

Comments
 (0)