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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c0c0f1c7bdb27b110b5feaac75671df9a675474bcc0577bac7ac250dc3c7e38
4
- data.tar.gz: 6a6d5faf960cafbdd6ae6fcb595c087f0bc4f456db9993c379bec390f1516ee1
3
+ metadata.gz: ed2de0b7d1cad971a52cc4c0a772c0de6227b6f797ac2e3ba2e6c033d5da4c7e
4
+ data.tar.gz: ff6bb32990d08f67a46b8052b31ff3d86ce328d0a0e5dc6b7434a46e2bf97b03
5
5
  SHA512:
6
- metadata.gz: 12ef06d5e9a3429b635f3c4c5b8042ef3bf974ee826de669f1d6fe0849809c53f743ae45f29f3bf469c9c02e7f12f16eeb1729f7f25d92d756ef6d0a86431552
7
- data.tar.gz: 827a38acc2ede092cc8dd6a2d5702b4338358b23d7e71a409b17000819405e991a2d3f6029c819b6ad0382befb83bad5b8f35e33cb506906adbe219fbfe90e44
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
- [![Build Status](https://travis-ci.org/lautis/rscsv.svg?branch=master)](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.5.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
- helix = "0.7.3"
12
- csv = "^1"
11
+ magnus = { version = "0.7", features = ["rb-sys"] }
12
+ csv = "1"
@@ -0,0 +1,5 @@
1
+ require "mkmf"
2
+ require "rb_sys/mkmf"
3
+
4
+ create_rust_makefile("rscsv/rscsv")
5
+
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Rscsv
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
data/lib/rscsv.rb CHANGED
@@ -1,6 +1,5 @@
1
- require 'helix_runtime'
2
- require 'rscsv/native'
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.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ville Lautanala
8
- autorequire:
9
- bindir: exe
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-31 00:00:00.000000000 Z
11
+ date: 2026-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: helix_runtime
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.7.5
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.7.5
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: '1.14'
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: '1.14'
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: '10.0'
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: '10.0'
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
- - Rakefile
97
- - ext/Makefile
98
- - ext/Rakefile
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
- rubyforge_project:
124
- rubygems_version: 2.7.7
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
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in rscsv.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- require 'bundler/gem_tasks'
2
- require 'bundler/setup'
3
- require 'rspec/core/rake_task'
4
- import 'lib/tasks/helix_runtime.rake'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task :spec => :build
9
- task :default => :spec
data/ext/Makefile DELETED
@@ -1,6 +0,0 @@
1
- all:
2
- cd .. && cargo rustc --release -- -C link-args=-Wl,-undefined,dynamic_lookup
3
- clean:
4
- rm -rf ../target
5
-
6
- install: ;
data/ext/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- Dir.chdir '..'
2
- import 'lib/tasks/helix_runtime.rake'
data/ext/extconf.rb DELETED
@@ -1,5 +0,0 @@
1
- if !system('cargo --version') || !system('rustc --version')
2
- raise 'You have to install Rust with Cargo (https://www.rust-lang.org/)'
3
- end
4
-
5
- require 'rake'
@@ -1,5 +0,0 @@
1
- require 'helix_runtime/build_task'
2
-
3
- HelixRuntime::BuildTask.new
4
-
5
- task :default => :build
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
- }