tracklib 0.1.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/lib.rs CHANGED
@@ -1,394 +1,44 @@
1
- use std::collections::{HashMap};
2
- use std::convert::{TryFrom};
3
- use std::io::{BufWriter};
4
- use lazy_static::lazy_static;
5
- use rutie::{
6
- class, methods, wrappable_struct, AnyObject, Array, Boolean, Class, Encoding, Float, Hash, Integer,
7
- Module, Object, RString, VM,
8
- };
9
- use rutie_serde::{ruby_class, rutie_serde_methods};
10
- use tracklib::{parse_rwtf, DataField, RWTFile, RWTFMetadata, TrackType};
1
+ mod polyline;
2
+ mod rwtfile;
3
+ mod surface;
11
4
 
12
- fn any_to_float(o: AnyObject) -> f64 {
13
- match o.try_convert_to::<Float>() {
14
- Ok(f) => f.to_f64(),
15
- Err(float_e) => o
16
- .try_convert_to::<Integer>()
17
- .map_err(|_| VM::raise_ex(float_e))
18
- .unwrap()
19
- .to_i64() as f64,
20
- }
21
- }
22
-
23
- fn any_to_int(o: AnyObject) -> i64 {
24
- match o.try_convert_to::<Integer>() {
25
- Ok(i) => i.to_i64(),
26
- Err(int_e) => o
27
- .try_convert_to::<Float>()
28
- .map_err(|_| VM::raise_ex(int_e))
29
- .unwrap()
30
- .to_f64() as i64,
31
- }
32
- }
33
-
34
- fn any_to_str(o: AnyObject) -> String {
35
- o.try_convert_to::<RString>()
36
- .map_err(|e| VM::raise_ex(e))
37
- .unwrap()
38
- .to_string()
39
- }
40
-
41
- fn any_to_bool(o: AnyObject) -> bool {
42
- o.try_convert_to::<Boolean>()
43
- .map_err(|e| VM::raise_ex(e))
44
- .unwrap()
45
- .to_bool()
46
- }
47
-
48
- fn any_to_ids(o: AnyObject) -> Vec<u64> {
49
- o.try_convert_to::<Array>()
50
- .map_err(|e| VM::raise_ex(e))
51
- .unwrap()
52
- .into_iter()
53
- .map(|ele| {
54
- ele.try_convert_to::<Integer>()
55
- .map_err(|e| VM::raise_ex(e))
56
- .unwrap()
57
- .to_u64()
58
- })
59
- .collect()
60
- }
61
-
62
- #[derive(Debug, Copy, Clone)]
63
- enum ColumnType {
64
- Numbers,
65
- LongFloat,
66
- ShortFloat,
67
- Base64,
68
- String,
69
- Bool,
70
- IDs,
71
- }
72
-
73
- impl ColumnType {
74
- fn from_str(name: &str) -> Option<Self> {
75
- match name {
76
- "Number" => Some(ColumnType::Numbers),
77
- "LongFloat" => Some(ColumnType::LongFloat),
78
- "ShortFloat" => Some(ColumnType::ShortFloat),
79
- "Base64" => Some(ColumnType::Base64),
80
- "String" => Some(ColumnType::String),
81
- "Bool" => Some(ColumnType::Bool),
82
- "IDs" => Some(ColumnType::IDs),
83
- _ => None
84
- }
85
- }
86
-
87
- fn exponent(&self) -> u8 {
88
- match self {
89
- ColumnType::Numbers => 48,
90
- ColumnType::LongFloat => 24,
91
- ColumnType::ShortFloat => 38,
92
- _ => {
93
- VM::raise(Class::from_existing("Exception"),
94
- &format!("can't handle numeric value for non-numeric field"));
95
- unreachable!();
96
- }
97
- }
98
- }
99
-
100
- fn max_integer(&self) -> i64 {
101
- 2i64.pow(u32::from(self.exponent()))
102
- }
103
-
104
- fn max_float(&self) -> f64 {
105
- 2f64.powi(i32::from(self.exponent()))
106
- }
107
- }
108
-
109
- fn convert_config(config: Hash) -> HashMap<String, ColumnType> {
110
- let mut hm = HashMap::new();
5
+ use rutie::{Class, Object};
111
6
 
112
- config.each(|rwtf_type_name, array_of_field_names| {
113
- let type_name_obj = rwtf_type_name
114
- .try_convert_to::<RString>()
115
- .map_err(|e| VM::raise_ex(e))
116
- .unwrap();
117
- let type_name_str = type_name_obj.to_str();
118
-
119
- if let Some(column_type) = ColumnType::from_str(type_name_str) {
120
- let array_obj = array_of_field_names
121
- .try_convert_to::<Array>()
122
- .map_err(|e| VM::raise_ex(e))
123
- .unwrap();
124
-
125
- for field_name in array_obj.into_iter() {
126
- let field_name_obj = field_name
127
- .try_convert_to::<RString>()
128
- .map_err(|e| VM::raise_ex(e))
129
- .unwrap();
130
-
131
- hm.insert(field_name_obj.to_string(), column_type);
132
- }
133
- } else {
134
- VM::raise(Class::from_existing("Exception"),
135
- &format!("unknown rwtf_field_config type: {}", type_name_str));
136
- unreachable!();
137
- }
7
+ #[allow(non_snake_case)]
8
+ #[no_mangle]
9
+ pub extern "C" fn Init_Tracklib() {
10
+ Class::new("RWTFile", Some(&Class::from_existing("Object"))).define(|itself| {
11
+ itself.def_self("from_bytes", rwtfile::rwtf_from_bytes);
12
+ itself.def_self("from_h", rwtfile::rwtf_from_hash);
13
+ itself.def("to_bytes", rwtfile::rwtf_to_bytes);
14
+ itself.def("to_h", rwtfile::rwtf_to_hash);
15
+ itself.def("metadata", rwtfile::rwtf_metadata);
16
+ itself.def("simplify_track_points", rwtfile::rwtf_simplify_track_points);
17
+ itself.def("inspect", rwtfile::rwtf_inspect);
18
+ itself.def("to_s", rwtfile::rwtf_inspect);
138
19
  });
139
20
 
140
- hm
141
- }
142
-
143
- fn is_empty_string(v: &AnyObject) -> bool {
144
- match v.try_convert_to::<RString>() {
145
- Ok(s) => s.to_str().is_empty(),
146
- Err(_) => false
147
- }
148
- }
149
-
150
- fn is_empty_array(v: &AnyObject) -> bool {
151
- match v.try_convert_to::<Array>() {
152
- Ok(a) => a.length() == 0,
153
- Err(_) => false
154
- }
155
- }
156
-
157
- fn is_number_and_too_large(v: &AnyObject, field_type: ColumnType) -> bool {
158
- // First we have to try to cast `v` to a numeric type (Integer or Float)
159
- // and then call to_i64/f64(). This will raise an exception if the number
160
- // is too large to turn into a primitive.
161
- let is_number_result = VM::protect(|| {
162
- match v.try_convert_to::<Integer>() {
163
- Ok(i) => {
164
- let _ = i.to_i64(); // force a conversion
165
- Boolean::new(true)
166
- }
167
- Err(_) => match v.try_convert_to::<Float>() {
168
- Ok(f) => {
169
- let _ = f.to_f64(); // force a conversion
170
- Boolean::new(true)
171
- }
172
- Err(_) => Boolean::new(false)
173
- }
174
- }.to_any_object()
21
+ Class::new("TracklibRoadClassMapping", Some(&Class::from_existing("Object"))).define(|itself| {
22
+ itself.def_self("new", surface::road_class_mapping_new);
23
+ itself.def("add_road_class", surface::road_class_mapping_add_road_class);
24
+ itself.def("to_s", surface::road_class_mapping_to_s);
175
25
  });
176
26
 
177
- match is_number_result {
178
- Ok(is_number) => {
179
- // Here we know that no exception was raised during the attempted primitive conversion.
180
- // We also know that `is_number` is a Boolean, so this unsafe cast is fine.
181
- if unsafe { is_number.to::<Boolean>().to_bool() } {
182
- match v.try_convert_to::<Integer>() {
183
- Ok(i) => i.to_i64().abs() > field_type.max_integer(),
184
- Err(_) => match v.try_convert_to::<Float>() {
185
- Ok(f) => f.to_f64().abs() > field_type.max_float(),
186
- Err(_) => false
187
- }
188
- }
189
- } else {
190
- false
191
- }
192
- }
193
- Err(_) => {
194
- VM::clear_error_info(); // clear ruby VM error register
195
- true // this IS a number and it IS too large
196
- }
197
- }
198
- }
199
-
200
- fn add_points(source: &Hash,
201
- section_points_config: &HashMap<String, ColumnType>,
202
- section_type: &str,
203
- mut callback: impl FnMut(usize, &str, DataField)) {
204
- let maybe_section_points = source
205
- .at(&RString::new_utf8(section_type))
206
- .try_convert_to::<Array>();
207
-
208
- if let Ok(section_points) = maybe_section_points {
209
- for (i, maybe_point) in section_points.into_iter().enumerate() {
210
- let point = maybe_point
211
- .try_convert_to::<Hash>()
212
- .map_err(|e| VM::raise_ex(e))
213
- .unwrap();
214
-
215
- point.each(|k: AnyObject, v: AnyObject| {
216
- let field_name_obj = k
217
- .try_convert_to::<RString>()
218
- .map_err(|e| VM::raise_ex(e))
219
- .unwrap();
220
- let name = field_name_obj.to_str();
221
-
222
- if !name.is_empty() {
223
- if let Some(field_type) = section_points_config.get(name) {
224
- if !v.is_nil()
225
- && !is_empty_string(&v)
226
- && !is_empty_array(&v)
227
- && !is_number_and_too_large(&v, *field_type)
228
- {
229
- let data = match field_type {
230
- ColumnType::LongFloat => DataField::LongFloat(any_to_float(v)),
231
- ColumnType::ShortFloat => DataField::ShortFloat(any_to_float(v)),
232
- ColumnType::Numbers => DataField::Number(any_to_int(v)),
233
- ColumnType::Base64 => DataField::Base64(any_to_str(v).replace("\n", "")),
234
- ColumnType::String => DataField::String(any_to_str(v)),
235
- ColumnType::Bool => DataField::Bool(any_to_bool(v)),
236
- ColumnType::IDs => DataField::IDs(any_to_ids(v)),
237
- };
238
-
239
- callback(i, name, data);
240
- }
241
- } else {
242
- VM::raise(Module::from_existing("Tracklib").get_nested_class("UnknownFieldError"),
243
- &format!("unknown {} field: {}", section_type, name));
244
- unreachable!();
245
- }
246
- }
247
- });
248
- }
249
- }
250
- }
251
-
252
- pub struct Inner {
253
- inner: RWTFile,
254
- }
255
-
256
- wrappable_struct!(Inner, InnerWrapper, INNER_WRAPPER);
257
-
258
- class!(RubyRWTFile);
259
-
260
- methods!(
261
- RubyRWTFile,
262
- itself,
263
-
264
- fn rwtf_from_bytes(bytes: RString) -> AnyObject {
265
- let source = bytes.map_err(|e| VM::raise_ex(e)).unwrap();
266
- let (_, rwtf) = parse_rwtf(source.to_bytes_unchecked())
267
- .map_err(|e| VM::raise(Class::from_existing("Exception"), &format!("{}", e)))
268
- .unwrap();
269
- let inner = Inner { inner: rwtf };
270
-
271
- Class::from_existing("RWTFile").wrap_data(inner, &*INNER_WRAPPER)
272
- }
273
-
274
- fn rwtf_to_bytes() -> RString {
275
- let mut writer = BufWriter::new(Vec::new());
276
- itself.get_data(&*INNER_WRAPPER).inner.write(&mut writer)
277
- .map_err(|e| VM::raise(Class::from_existing("Exception"), &format!("{}", e)))
278
- .unwrap();
279
-
280
- let encoding = Encoding::find("ASCII-8BIT")
281
- .map_err(|e| VM::raise_ex(e))
282
- .unwrap();
283
-
284
- let buf = writer.into_inner()
285
- .map_err(|e| VM::raise(Class::from_existing("Exception"), &format!("{}", e)))
286
- .unwrap();
287
-
288
- RString::from_bytes(&buf, &encoding)
289
- }
290
-
291
- fn rwtf_from_hash(input: Hash, config_input: Hash, metadata: Hash) -> AnyObject {
292
- let source = input.map_err(|e| VM::raise_ex(e)).unwrap();
293
- let config = config_input.map_err(|e| VM::raise_ex(e)).unwrap();
294
- let track_points_config = convert_config(config.at(&RString::new_utf8("track_points"))
295
- .try_convert_to::<Hash>()
296
- .map_err(|e| VM::raise_ex(e))
297
- .unwrap());
298
- let course_points_config = convert_config(config.at(&RString::new_utf8("course_points"))
299
- .try_convert_to::<Hash>()
300
- .map_err(|e| VM::raise_ex(e))
301
- .unwrap());
302
-
303
- let mut rwtf = if let Some(md) = metadata.ok() {
304
- let tt_metadata = md.at(&RString::new_utf8("track_type"))
305
- .try_convert_to::<Hash>()
306
- .map_err(|e| VM::raise_ex(e))
307
- .unwrap();
308
-
309
- let track_type = tt_metadata.at(&RString::new_utf8("type"))
310
- .try_convert_to::<RString>()
311
- .map_err(|e| VM::raise_ex(e))
312
- .unwrap();
313
-
314
- let id = u32::try_from(tt_metadata.at(&RString::new_utf8("id"))
315
- .try_convert_to::<Integer>()
316
- .map_err(|e| VM::raise_ex(e))
317
- .unwrap()
318
- .to_u64())
319
- .map_err(|e| VM::raise(Class::from_existing("Exception"), &format!("{}", e)))
320
- .unwrap();
321
-
322
- let tt = match track_type.to_str() {
323
- "trip" => TrackType::Trip(id),
324
- "route" => TrackType::Route(id),
325
- "segment" => TrackType::Segment(id),
326
- _ => {
327
- VM::raise(
328
- Class::from_existing("Exception"),
329
- &format!("unknown track_type metadata: {}", track_type.to_str()),
330
- );
331
- unreachable!();
332
- }
333
- };
334
-
335
- RWTFile::with_track_type(tt)
336
- } else {
337
- RWTFile::new()
338
- };
339
-
340
- add_points(&source, &track_points_config, "track_points", |i, name, data| {
341
- rwtf.add_track_point(i, name, data)
342
- .map_err(|e| {
343
- VM::raise(Class::from_existing("Exception"), &format!("{}", e))
344
- })
345
- .unwrap();
346
- });
347
-
348
- add_points(&source, &course_points_config, "course_points", |i, name, data| {
349
- rwtf.add_course_point(i, name, data)
350
- .map_err(|e| {
351
- VM::raise(Class::from_existing("Exception"), &format!("{}", e))
352
- })
353
- .unwrap();
354
- });
355
-
356
- let inner = Inner { inner: rwtf };
357
- Class::from_existing("RWTFile").wrap_data(inner, &*INNER_WRAPPER)
358
- }
359
-
360
- fn rwtf_inspect() -> RString {
361
- let rwtf = &itself.get_data(&*INNER_WRAPPER).inner;
362
-
363
- RString::new_utf8(&format!("RWTFile<track_points: {}, course_points: {}>",
364
- rwtf.track_points.len(),
365
- rwtf.course_points.len()))
366
- }
367
- );
368
-
369
- rutie_serde_methods!(
370
- RubyRWTFile,
371
- itself,
372
- ruby_class!(Exception),
373
-
374
- fn rwtf_to_hash() -> &RWTFile {
375
- &itself.get_data(&*INNER_WRAPPER).inner
376
- }
377
-
378
- fn rwtf_metadata() -> &RWTFMetadata {
379
- &itself.get_data(&*INNER_WRAPPER).inner.metadata()
380
- }
381
- );
27
+ Class::new("TracklibSurfaceMapping", Some(&Class::from_existing("Object"))).define(|itself| {
28
+ itself.def_self("new", surface::surface_mapping_new);
29
+ itself.def("add_surface", surface::surface_mapping_add_surface);
30
+ itself.def("add_road_class_mapping", surface::surface_mapping_add_road_class_mapping);
31
+ itself.def("to_s", surface::surface_mapping_to_s);
32
+ });
382
33
 
383
- #[allow(non_snake_case)]
384
- #[no_mangle]
385
- pub extern "C" fn Init_Tracklib() {
386
- Class::new("RWTFile", Some(&Class::from_existing("Object"))).define(|itself| {
387
- itself.def_self("from_bytes", rwtf_from_bytes);
388
- itself.def_self("from_h", rwtf_from_hash);
389
- itself.def("to_bytes", rwtf_to_bytes);
390
- itself.def("to_h", rwtf_to_hash);
391
- itself.def("metadata", rwtf_metadata);
392
- itself.def("inspect", rwtf_inspect);
34
+ Class::new("TracklibFieldEncodeOptionsVec", Some(&Class::from_existing("Object"))).define(|itself| {
35
+ itself.def_self("new", polyline::field_encode_options_vec_new);
36
+ itself.def("add_y", polyline::field_encode_options_vec_add_field_y);
37
+ itself.def("add_x", polyline::field_encode_options_vec_add_field_x);
38
+ itself.def("add_d", polyline::field_encode_options_vec_add_field_d);
39
+ itself.def("add_e", polyline::field_encode_options_vec_add_field_e);
40
+ itself.def("add_s", polyline::field_encode_options_vec_add_field_s);
41
+ itself.def("add_r", polyline::field_encode_options_vec_add_field_r);
42
+ itself.def("to_s", polyline::field_encode_options_vec_to_s);
393
43
  });
394
44
  }
data/src/polyline.rs ADDED
@@ -0,0 +1,95 @@
1
+ use lazy_static::lazy_static;
2
+ use rutie::{AnyException, AnyObject, Class, Integer, NilClass, Object, RString, VM, VerifiedObject, class, methods, wrappable_struct};
3
+ use tracklib::{FieldEncodeOptions, PointField};
4
+ use std::convert::TryFrom;
5
+
6
+ fn add_field_helper(v: &mut Vec<FieldEncodeOptions>, field: PointField, precision: Result<Integer, AnyException>) {
7
+ let prec = u32::try_from(precision.map_err(|e| VM::raise_ex(e)).unwrap().to_i32())
8
+ .map_err(|_| VM::raise(Class::from_existing("Exception"), "Precision must be a u32")).unwrap();
9
+ v.push(FieldEncodeOptions::new(field, prec))
10
+ }
11
+
12
+ pub struct FieldEncodeOptionsVecInner {
13
+ inner: Vec<FieldEncodeOptions>,
14
+ }
15
+
16
+ wrappable_struct!(
17
+ FieldEncodeOptionsVecInner,
18
+ FieldEncodeOptionsVecInnerWrapper,
19
+ FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER
20
+ );
21
+
22
+ class!(RubyFieldEncodeOptionsVec);
23
+
24
+ methods!(
25
+ RubyFieldEncodeOptionsVec,
26
+ itself,
27
+
28
+ fn field_encode_options_vec_new() -> AnyObject {
29
+ let inner = FieldEncodeOptionsVecInner {
30
+ inner: Vec::new()
31
+ };
32
+
33
+ Class::from_existing("TracklibFieldEncodeOptionsVec").wrap_data(inner, &*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER)
34
+ }
35
+
36
+ fn field_encode_options_vec_add_field_y(precision: Integer) -> NilClass {
37
+ let inner = &mut itself.get_data_mut(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
38
+ add_field_helper(inner, PointField::Y, precision);
39
+ NilClass::new()
40
+ }
41
+
42
+ fn field_encode_options_vec_add_field_x(precision: Integer) -> NilClass {
43
+ let inner = &mut itself.get_data_mut(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
44
+ add_field_helper(inner, PointField::X, precision);
45
+ NilClass::new()
46
+ }
47
+
48
+ fn field_encode_options_vec_add_field_d(precision: Integer) -> NilClass {
49
+ let inner = &mut itself.get_data_mut(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
50
+ add_field_helper(inner, PointField::D, precision);
51
+ NilClass::new()
52
+ }
53
+
54
+ fn field_encode_options_vec_add_field_e(precision: Integer) -> NilClass {
55
+ let inner = &mut itself.get_data_mut(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
56
+ add_field_helper(inner, PointField::E, precision);
57
+ NilClass::new()
58
+ }
59
+
60
+ fn field_encode_options_vec_add_field_s(default_surface: Integer, precision: Integer) -> NilClass {
61
+ let default_surface_id = default_surface.map_err(|e| VM::raise_ex(e)).unwrap().to_i64();
62
+ let inner = &mut itself.get_data_mut(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
63
+ add_field_helper(inner, PointField::S(default_surface_id), precision);
64
+ NilClass::new()
65
+ }
66
+
67
+ fn field_encode_options_vec_add_field_r(default_road_class: Integer, precision: Integer) -> NilClass {
68
+ let default_road_class_id = default_road_class.map_err(|e| VM::raise_ex(e)).unwrap().to_i64();
69
+ let inner = &mut itself.get_data_mut(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
70
+ add_field_helper(inner, PointField::R(default_road_class_id), precision);
71
+ NilClass::new()
72
+ }
73
+
74
+ fn field_encode_options_vec_to_s() -> RString {
75
+ let inner = &itself.get_data(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner;
76
+
77
+ RString::new_utf8(&format!("{:?}", inner))
78
+ }
79
+ );
80
+
81
+ impl RubyFieldEncodeOptionsVec {
82
+ pub fn inner(&self) -> &[FieldEncodeOptions] {
83
+ &self.get_data(&*FIELD_ENCODE_OPTIONS_VEC_INNER_WRAPPER).inner
84
+ }
85
+ }
86
+
87
+ impl VerifiedObject for RubyFieldEncodeOptionsVec {
88
+ fn is_correct_type<T: Object>(object: &T) -> bool {
89
+ Class::from_existing("TracklibFieldEncodeOptionsVec").case_equals(object)
90
+ }
91
+
92
+ fn error_message() -> &'static str {
93
+ "Error converting to FieldEncodeOptionsVec"
94
+ }
95
+ }