rscsv 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/README.md +0 -2
- data/{Cargo.toml → ext/rscsv/Cargo.toml} +4 -4
- data/ext/rscsv/extconf.rb +5 -0
- data/ext/rscsv/src/lib.rs +146 -0
- data/lib/rscsv/version.rb +1 -1
- data/lib/rscsv.rb +3 -3
- metadata +63 -28
- data/Cargo.lock +0 -75
- data/Gemfile +0 -4
- data/Rakefile +0 -9
- data/ext/Makefile +0 -6
- data/ext/Rakefile +0 -2
- data/ext/extconf.rb +0 -5
- data/lib/tasks/helix_runtime.rake +0 -5
- data/src/lib.rs +0 -206
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed2de0b7d1cad971a52cc4c0a772c0de6227b6f797ac2e3ba2e6c033d5da4c7e
|
|
4
|
+
data.tar.gz: ff6bb32990d08f67a46b8052b31ff3d86ce328d0a0e5dc6b7434a46e2bf97b03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd89acf93a303da3ba3670208392bed3d891b8659a1fcc317790a1c23510d5a0017cac6cd1e2aa8932d3cfdd13ee08f46bba5ff6cccf3767978e290349f4d574
|
|
7
|
+
data.tar.gz: 5790f8f86286de1cf06630bc2934c126099bbe83fbd973982956501555a849abed0db6250b66441adb71983761ddcb1c626ce0473ca080c0b00ec119cc7efc0e
|
data/README.md
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
Fast CSV using Rust extensions. Can read arrays of arrays from strings and write
|
|
4
4
|
strings from arrays of arrays.
|
|
5
5
|
|
|
6
|
-
[](https://travis-ci.org/lautis/rscsv)
|
|
7
|
-
|
|
8
6
|
## Installation
|
|
9
7
|
|
|
10
8
|
This gem requires Rust (~> 1.17) and Cargo to be installed. With those
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rscsv"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
authors = ["Ville Lautanala <lautis@gmail.com>"]
|
|
5
|
+
edition = "2021"
|
|
5
6
|
|
|
6
7
|
[lib]
|
|
7
|
-
|
|
8
8
|
crate-type = ["cdylib"]
|
|
9
9
|
|
|
10
10
|
[dependencies]
|
|
11
|
-
|
|
12
|
-
csv = "
|
|
11
|
+
magnus = { version = "0.7", features = ["rb-sys"] }
|
|
12
|
+
csv = "1"
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
use magnus::{
|
|
2
|
+
block::yield_value, function, prelude::*, Error, RArray, RString, Ruby, Value,
|
|
3
|
+
};
|
|
4
|
+
use std::io::Read;
|
|
5
|
+
|
|
6
|
+
fn generate_lines(rows: Vec<Vec<String>>) -> Result<String, Error> {
|
|
7
|
+
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
|
8
|
+
for row in rows {
|
|
9
|
+
wtr.write_record(&row)
|
|
10
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let inner = wtr
|
|
14
|
+
.into_inner()
|
|
15
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
16
|
+
|
|
17
|
+
String::from_utf8(inner)
|
|
18
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fn record_to_ruby_array(record: &csv::ByteRecord) -> Result<RArray, Error> {
|
|
22
|
+
let array = RArray::with_capacity(record.len());
|
|
23
|
+
for column in record.iter() {
|
|
24
|
+
let column_str = RString::from_slice(column);
|
|
25
|
+
array.push(column_str)?;
|
|
26
|
+
}
|
|
27
|
+
Ok(array)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
struct EnumeratorRead {
|
|
31
|
+
enumerator: Value,
|
|
32
|
+
buffer: Option<Vec<u8>>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl EnumeratorRead {
|
|
36
|
+
fn new(enumerator: Value) -> Self {
|
|
37
|
+
EnumeratorRead {
|
|
38
|
+
enumerator,
|
|
39
|
+
buffer: None,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn read_and_store_overflow(&mut self, buf: &mut [u8], value: &[u8]) -> std::io::Result<usize> {
|
|
44
|
+
if value.len() > buf.len() {
|
|
45
|
+
let (current, next) = value.split_at(buf.len());
|
|
46
|
+
buf.copy_from_slice(current);
|
|
47
|
+
self.buffer = Some(next.to_vec());
|
|
48
|
+
Ok(current.len())
|
|
49
|
+
} else {
|
|
50
|
+
buf[..value.len()].copy_from_slice(value);
|
|
51
|
+
self.buffer = None;
|
|
52
|
+
Ok(value.len())
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn read_from_external(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
57
|
+
let result: Result<String, Error> = self.enumerator.funcall("next", ());
|
|
58
|
+
match result {
|
|
59
|
+
Ok(string) => self.read_and_store_overflow(buf, string.as_bytes()),
|
|
60
|
+
Err(_) => {
|
|
61
|
+
// StopIteration or other exception - signal EOF
|
|
62
|
+
Ok(0)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
impl Read for EnumeratorRead {
|
|
69
|
+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
70
|
+
match self.buffer.take() {
|
|
71
|
+
Some(ref inner) => self.read_and_store_overflow(buf, inner),
|
|
72
|
+
None => self.read_from_external(buf),
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fn csv_reader<R: Read>(reader: R) -> csv::Reader<R> {
|
|
78
|
+
csv::ReaderBuilder::new()
|
|
79
|
+
.buffer_capacity(16 * 1024)
|
|
80
|
+
.has_headers(false)
|
|
81
|
+
.from_reader(reader)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn yield_csv(enumerator: Value) -> Result<(), Error> {
|
|
85
|
+
let mut reader = csv_reader(EnumeratorRead::new(enumerator));
|
|
86
|
+
let mut record = csv::ByteRecord::new();
|
|
87
|
+
|
|
88
|
+
loop {
|
|
89
|
+
let has_record = reader
|
|
90
|
+
.read_byte_record(&mut record)
|
|
91
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
92
|
+
|
|
93
|
+
if !has_record {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let row_array = record_to_ruby_array(&record)?;
|
|
98
|
+
let _: Value = yield_value(row_array)?;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Ok(())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn parse_csv(data: String) -> Result<RArray, Error> {
|
|
105
|
+
let mut reader = csv_reader(data.as_bytes());
|
|
106
|
+
let result = RArray::new();
|
|
107
|
+
|
|
108
|
+
for record in reader.records() {
|
|
109
|
+
let record = record
|
|
110
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
111
|
+
|
|
112
|
+
let row = RArray::with_capacity(record.len());
|
|
113
|
+
for field in record.iter() {
|
|
114
|
+
row.push(RString::new(field))?;
|
|
115
|
+
}
|
|
116
|
+
result.push(row)?;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
Ok(result)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fn generate_line(row: Vec<String>) -> Result<String, Error> {
|
|
123
|
+
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
|
124
|
+
wtr.write_record(&row)
|
|
125
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
126
|
+
|
|
127
|
+
let inner = wtr
|
|
128
|
+
.into_inner()
|
|
129
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
130
|
+
|
|
131
|
+
String::from_utf8(inner)
|
|
132
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#[magnus::init]
|
|
136
|
+
fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
137
|
+
let reader_class = ruby.define_class("RscsvReader", ruby.class_object())?;
|
|
138
|
+
reader_class.define_singleton_method("each_internal", function!(yield_csv, 1))?;
|
|
139
|
+
reader_class.define_singleton_method("parse", function!(parse_csv, 1))?;
|
|
140
|
+
|
|
141
|
+
let writer_class = ruby.define_class("RscsvWriter", ruby.class_object())?;
|
|
142
|
+
writer_class.define_singleton_method("generate_line", function!(generate_line, 1))?;
|
|
143
|
+
writer_class.define_singleton_method("generate_lines", function!(generate_lines, 1))?;
|
|
144
|
+
|
|
145
|
+
Ok(())
|
|
146
|
+
}
|
data/lib/rscsv/version.rb
CHANGED
data/lib/rscsv.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'rscsv/version'
|
|
1
|
+
require_relative 'rscsv/rscsv'
|
|
2
|
+
require_relative 'rscsv/version'
|
|
4
3
|
|
|
5
4
|
module Rscsv
|
|
6
5
|
Reader = RscsvReader
|
|
@@ -12,5 +11,6 @@ module Rscsv
|
|
|
12
11
|
nil
|
|
13
12
|
end
|
|
14
13
|
end
|
|
14
|
+
|
|
15
15
|
Writer = RscsvWriter
|
|
16
16
|
end
|
metadata
CHANGED
|
@@ -1,57 +1,85 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rscsv
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ville Lautanala
|
|
8
|
-
autorequire:
|
|
9
|
-
bindir:
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: rb_sys
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: '0.9'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: '0.9'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '2.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '2.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rake
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '13.0'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '13.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake-compiler
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.2'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.2'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake-compiler-dock
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '1.5'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '1.5'
|
|
55
83
|
- !ruby/object:Gem::Dependency
|
|
56
84
|
name: rspec
|
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,6 +94,20 @@ dependencies:
|
|
|
66
94
|
- - "~>"
|
|
67
95
|
- !ruby/object:Gem::Version
|
|
68
96
|
version: '3.0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: csv
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '3.0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3.0'
|
|
69
111
|
- !ruby/object:Gem::Dependency
|
|
70
112
|
name: benchmark-ips
|
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -85,27 +127,21 @@ email:
|
|
|
85
127
|
- lautis@gmail.com
|
|
86
128
|
executables: []
|
|
87
129
|
extensions:
|
|
88
|
-
- ext/extconf.rb
|
|
130
|
+
- ext/rscsv/extconf.rb
|
|
89
131
|
extra_rdoc_files: []
|
|
90
132
|
files:
|
|
91
|
-
- Cargo.lock
|
|
92
|
-
- Cargo.toml
|
|
93
|
-
- Gemfile
|
|
94
133
|
- LICENSE.txt
|
|
95
134
|
- README.md
|
|
96
|
-
-
|
|
97
|
-
- ext/
|
|
98
|
-
- ext/
|
|
99
|
-
- ext/extconf.rb
|
|
135
|
+
- ext/rscsv/Cargo.toml
|
|
136
|
+
- ext/rscsv/extconf.rb
|
|
137
|
+
- ext/rscsv/src/lib.rs
|
|
100
138
|
- lib/rscsv.rb
|
|
101
139
|
- lib/rscsv/version.rb
|
|
102
|
-
- lib/tasks/helix_runtime.rake
|
|
103
|
-
- src/lib.rs
|
|
104
140
|
homepage: https://github.com/lautis/rscsv
|
|
105
141
|
licenses:
|
|
106
142
|
- MIT
|
|
107
143
|
metadata: {}
|
|
108
|
-
post_install_message:
|
|
144
|
+
post_install_message:
|
|
109
145
|
rdoc_options: []
|
|
110
146
|
require_paths:
|
|
111
147
|
- lib
|
|
@@ -113,16 +149,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
113
149
|
requirements:
|
|
114
150
|
- - ">="
|
|
115
151
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '0'
|
|
152
|
+
version: '3.0'
|
|
117
153
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
154
|
requirements:
|
|
119
155
|
- - ">="
|
|
120
156
|
- !ruby/object:Gem::Version
|
|
121
157
|
version: '0'
|
|
122
158
|
requirements: []
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
signing_key:
|
|
159
|
+
rubygems_version: 3.5.22
|
|
160
|
+
signing_key:
|
|
126
161
|
specification_version: 4
|
|
127
162
|
summary: Rust-powered CSV
|
|
128
163
|
test_files: []
|
data/Cargo.lock
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
[[package]]
|
|
2
|
-
name = "cstr-macro"
|
|
3
|
-
version = "0.1.0"
|
|
4
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5
|
-
|
|
6
|
-
[[package]]
|
|
7
|
-
name = "csv"
|
|
8
|
-
version = "1.0.5"
|
|
9
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
10
|
-
dependencies = [
|
|
11
|
-
"csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
12
|
-
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
[[package]]
|
|
16
|
-
name = "csv-core"
|
|
17
|
-
version = "0.1.5"
|
|
18
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
19
|
-
dependencies = [
|
|
20
|
-
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
[[package]]
|
|
24
|
-
name = "helix"
|
|
25
|
-
version = "0.7.5"
|
|
26
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
-
dependencies = [
|
|
28
|
-
"cstr-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
29
|
-
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
30
|
-
"libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
[[package]]
|
|
34
|
-
name = "libc"
|
|
35
|
-
version = "0.2.51"
|
|
36
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
37
|
-
|
|
38
|
-
[[package]]
|
|
39
|
-
name = "libcruby-sys"
|
|
40
|
-
version = "0.7.5"
|
|
41
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
-
dependencies = [
|
|
43
|
-
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
[[package]]
|
|
47
|
-
name = "memchr"
|
|
48
|
-
version = "2.2.0"
|
|
49
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
50
|
-
dependencies = [
|
|
51
|
-
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
[[package]]
|
|
55
|
-
name = "rscsv"
|
|
56
|
-
version = "0.5.0"
|
|
57
|
-
dependencies = [
|
|
58
|
-
"csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
59
|
-
"helix 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
[[package]]
|
|
63
|
-
name = "serde"
|
|
64
|
-
version = "1.0.89"
|
|
65
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
66
|
-
|
|
67
|
-
[metadata]
|
|
68
|
-
"checksum cstr-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db53fddba18cdd35477a7213a3ef6acfbfa333c31b42ce019e544c4a1420a06f"
|
|
69
|
-
"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04"
|
|
70
|
-
"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65"
|
|
71
|
-
"checksum helix 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49a017e3e798ad9386e0a0584e66fd6c04a80ccc1242eb8f689c62ce6f408240"
|
|
72
|
-
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
|
|
73
|
-
"checksum libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fef6028cdce0c8d55676fd1d66bb810facef8cade0dd71d28511d375e84da4c0"
|
|
74
|
-
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
|
|
75
|
-
"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
|
data/Gemfile
DELETED
data/Rakefile
DELETED
data/ext/Makefile
DELETED
data/ext/Rakefile
DELETED
data/ext/extconf.rb
DELETED
data/src/lib.rs
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#[macro_use]
|
|
2
|
-
extern crate helix;
|
|
3
|
-
extern crate csv;
|
|
4
|
-
|
|
5
|
-
use std::error::Error;
|
|
6
|
-
use std::io::Read;
|
|
7
|
-
use helix::sys;
|
|
8
|
-
use helix::sys::{VALUE, RubyException};
|
|
9
|
-
use helix::{FromRuby, CheckResult, ToRuby};
|
|
10
|
-
use helix::libc::{c_void};
|
|
11
|
-
|
|
12
|
-
fn generate_lines(rows: &[Vec<String>]) -> Result<String, Box<Error>> {
|
|
13
|
-
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
|
14
|
-
for row in rows {
|
|
15
|
-
wtr.write_record(row)?;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
Ok(String::from_utf8(wtr.into_inner()?)?)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
fn record_to_ruby(record: &csv::ByteRecord) -> VALUE {
|
|
22
|
-
let inner_array = unsafe { sys::rb_ary_new_capa(record.len() as isize) };
|
|
23
|
-
for column in record.iter() {
|
|
24
|
-
unsafe {
|
|
25
|
-
let column_value =
|
|
26
|
-
sys::rb_utf8_str_new(column.as_ptr() as *const i8, column.len() as i64);
|
|
27
|
-
sys::rb_ary_push(inner_array, column_value);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
inner_array
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
extern fn protect_wrapper<F>(closure: *mut c_void) -> VALUE
|
|
34
|
-
where F: FnOnce() -> VALUE {
|
|
35
|
-
let closure_option = closure as *mut Option<F>;
|
|
36
|
-
unsafe {
|
|
37
|
-
(*closure_option).take().unwrap()()
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
pub fn protect<F>(func: F) -> Result<VALUE, RubyException>
|
|
42
|
-
where
|
|
43
|
-
F: FnOnce() -> VALUE,
|
|
44
|
-
{
|
|
45
|
-
let mut state = sys::EMPTY_EXCEPTION;
|
|
46
|
-
let value = unsafe {
|
|
47
|
-
sys::rb_protect(
|
|
48
|
-
protect_wrapper::<F>,
|
|
49
|
-
&func as *const _ as *mut c_void,
|
|
50
|
-
&mut state,
|
|
51
|
-
)
|
|
52
|
-
};
|
|
53
|
-
if state == sys::EMPTY_EXCEPTION {
|
|
54
|
-
Ok(value)
|
|
55
|
-
} else {
|
|
56
|
-
Err(state)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
struct Enumerator {
|
|
61
|
-
value: VALUE,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
impl FromRuby for Enumerator {
|
|
65
|
-
type Checked = Enumerator;
|
|
66
|
-
|
|
67
|
-
fn from_ruby(value: VALUE) -> CheckResult<Enumerator> {
|
|
68
|
-
// TODO: validate this?
|
|
69
|
-
Ok(Enumerator { value })
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
fn from_checked(checked: Enumerator) -> Enumerator {
|
|
73
|
-
checked
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
struct EnumeratorRead {
|
|
78
|
-
value: VALUE,
|
|
79
|
-
next: Option<Vec<u8>>,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
impl EnumeratorRead {
|
|
83
|
-
fn new(value: VALUE) -> EnumeratorRead {
|
|
84
|
-
EnumeratorRead {
|
|
85
|
-
value,
|
|
86
|
-
next: None,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
fn read_and_store_overflow(&mut self, buf: &mut [u8], value: &[u8]) -> std::io::Result<usize> {
|
|
91
|
-
if value.len() > buf.len() {
|
|
92
|
-
match value.split_at(buf.len()) {
|
|
93
|
-
(current, next) => {
|
|
94
|
-
for (index, c) in current.iter().enumerate() {
|
|
95
|
-
buf[index] = *c;
|
|
96
|
-
}
|
|
97
|
-
self.next = Some(next.to_vec());
|
|
98
|
-
Ok(current.len())
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
} else {
|
|
103
|
-
for (index, value) in value.iter().enumerate() {
|
|
104
|
-
buf[index] = *value;
|
|
105
|
-
}
|
|
106
|
-
self.next = None;
|
|
107
|
-
Ok(value.len() as usize)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
fn read_from_external(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
112
|
-
|
|
113
|
-
let value = self.value;
|
|
114
|
-
let result = protect(|| {
|
|
115
|
-
unsafe { sys::rb_funcall(
|
|
116
|
-
value,
|
|
117
|
-
sys::rb_intern("next\0".as_ptr() as *const i8),
|
|
118
|
-
0)
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
match result {
|
|
122
|
-
Ok(next) => {
|
|
123
|
-
let string = String::from_ruby_unwrap(next);
|
|
124
|
-
self.read_and_store_overflow(buf, string.as_bytes())
|
|
125
|
-
},
|
|
126
|
-
Err(state) => {
|
|
127
|
-
unsafe { sys::rb_jump_tag(state) };
|
|
128
|
-
//Err(std::io::Error::new(ErrorKind::Other, "Ruby Exception"))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
impl Read for EnumeratorRead {
|
|
136
|
-
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
137
|
-
match self.next.take() {
|
|
138
|
-
Some(ref inner) => self.read_and_store_overflow(buf, inner),
|
|
139
|
-
None => self.read_from_external(buf),
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
fn csv_reader<R: Read>(reader: R) -> csv::Reader<R> {
|
|
145
|
-
csv::ReaderBuilder::new()
|
|
146
|
-
.buffer_capacity(16 * 1024)
|
|
147
|
-
.has_headers(false)
|
|
148
|
-
.from_reader(reader)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
fn yield_csv(data: &Enumerator) -> Result<(), csv::Error> {
|
|
152
|
-
let mut reader = csv_reader(EnumeratorRead::new(data.value));
|
|
153
|
-
let mut record = csv::ByteRecord::new();
|
|
154
|
-
|
|
155
|
-
while reader.read_byte_record(&mut record)? {
|
|
156
|
-
let inner_array = record_to_ruby(&record);
|
|
157
|
-
let result = protect(|| {
|
|
158
|
-
unsafe {
|
|
159
|
-
return sys::rb_yield(inner_array);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if result.is_err() {
|
|
164
|
-
unsafe { sys::rb_jump_tag(result.unwrap_err()) };
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
Ok(())
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
fn parse_csv(data: &str) -> Result<Vec<Vec<VALUE>>, csv::Error> {
|
|
172
|
-
csv_reader(data.as_bytes())
|
|
173
|
-
.records()
|
|
174
|
-
.map(|r| r.map(|v| record_to_vec(&v)))
|
|
175
|
-
.collect()
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
fn record_to_vec(record: &csv::StringRecord) -> Vec<VALUE> {
|
|
179
|
-
record.iter().map(|s| s.to_ruby().unwrap()).collect()
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
ruby! {
|
|
183
|
-
class RscsvReader {
|
|
184
|
-
def each_internal(data: Enumerator) -> Result<(), &'static str> {
|
|
185
|
-
yield_csv(&data).map_err(|_| "Error parsing CSV")
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
def parse(data: String) -> Result<Vec<Vec<VALUE>>, &'static str> {
|
|
189
|
-
parse_csv(&data).map_err(|_| "Error parsing CSV")
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
class RscsvWriter {
|
|
194
|
-
def generate_line(row: Vec<String>) -> Result<String, &'static str> {
|
|
195
|
-
let mut wtr = csv::WriterBuilder::new().from_writer(vec![]);
|
|
196
|
-
|
|
197
|
-
wtr.write_record(&row)
|
|
198
|
-
.map(|_| String::from_utf8(wtr.into_inner().unwrap()).unwrap())
|
|
199
|
-
.map_err(|_| "Error generating csv")
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
def generate_lines(rows: Vec<Vec<String>>) -> Result<String, &'static str> {
|
|
203
|
-
generate_lines(&rows).map_err(|_| "Error generating csv")
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|