rscsv 0.6.0.beta4 → 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 +65 -30
- data/Cargo.lock +0 -89
- 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
|
-
version:
|
|
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,89 +0,0 @@
|
|
|
1
|
-
[[package]]
|
|
2
|
-
name = "cfg-if"
|
|
3
|
-
version = "0.1.6"
|
|
4
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5
|
-
|
|
6
|
-
[[package]]
|
|
7
|
-
name = "cstr-macro"
|
|
8
|
-
version = "0.1.0"
|
|
9
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
10
|
-
|
|
11
|
-
[[package]]
|
|
12
|
-
name = "csv"
|
|
13
|
-
version = "1.0.5"
|
|
14
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
-
dependencies = [
|
|
16
|
-
"csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
17
|
-
"serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
[[package]]
|
|
21
|
-
name = "csv-core"
|
|
22
|
-
version = "0.1.5"
|
|
23
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
-
dependencies = [
|
|
25
|
-
"memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
[[package]]
|
|
29
|
-
name = "helix"
|
|
30
|
-
version = "0.7.5"
|
|
31
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
32
|
-
dependencies = [
|
|
33
|
-
"cstr-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
34
|
-
"libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
35
|
-
"libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
[[package]]
|
|
39
|
-
name = "libc"
|
|
40
|
-
version = "0.2.46"
|
|
41
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
-
|
|
43
|
-
[[package]]
|
|
44
|
-
name = "libcruby-sys"
|
|
45
|
-
version = "0.7.5"
|
|
46
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
47
|
-
dependencies = [
|
|
48
|
-
"libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
[[package]]
|
|
52
|
-
name = "memchr"
|
|
53
|
-
version = "2.1.2"
|
|
54
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
-
dependencies = [
|
|
56
|
-
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
57
|
-
"libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
58
|
-
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
[[package]]
|
|
62
|
-
name = "rscsv"
|
|
63
|
-
version = "0.5.0"
|
|
64
|
-
dependencies = [
|
|
65
|
-
"csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
66
|
-
"helix 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
67
|
-
]
|
|
68
|
-
|
|
69
|
-
[[package]]
|
|
70
|
-
name = "serde"
|
|
71
|
-
version = "1.0.84"
|
|
72
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
73
|
-
|
|
74
|
-
[[package]]
|
|
75
|
-
name = "version_check"
|
|
76
|
-
version = "0.1.5"
|
|
77
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
78
|
-
|
|
79
|
-
[metadata]
|
|
80
|
-
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
|
81
|
-
"checksum cstr-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db53fddba18cdd35477a7213a3ef6acfbfa333c31b42ce019e544c4a1420a06f"
|
|
82
|
-
"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04"
|
|
83
|
-
"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65"
|
|
84
|
-
"checksum helix 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49a017e3e798ad9386e0a0584e66fd6c04a80ccc1242eb8f689c62ce6f408240"
|
|
85
|
-
"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd"
|
|
86
|
-
"checksum libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fef6028cdce0c8d55676fd1d66bb810facef8cade0dd71d28511d375e84da4c0"
|
|
87
|
-
"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
|
|
88
|
-
"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7"
|
|
89
|
-
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
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
|
-
}
|