tracklib 0.1.7 → 0.3.0

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.
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
+ }