altsv 0.0.2
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 +7 -0
- data/.gitignore +57 -0
- data/.travis.yml +7 -0
- data/Cargo.lock +41 -0
- data/Cargo.toml +11 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +78 -0
- data/Rakefile +5 -0
- data/altsv.gemspec +45 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/altsv/extconf.rb +25 -0
- data/lib/altsv.rb +38 -0
- data/lib/altsv/version.rb +3 -0
- data/src/lib.rs +356 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6dabb0913e3f6ee313714b955983a1b52a6ca3dfd3a567287cb08397184a8ffa
|
4
|
+
data.tar.gz: '090396592332217d2c087c8e8944b1b5cad271a08f5f2231de01b41002fc1422'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a799aae3dd58843d7b7e70ebfdc5f48099e850fbff88d92e003e9615a1d9e86597ea9da95703ccc1377264bea9a3a5790bfb521761640e0b5d40775318d89175
|
7
|
+
data.tar.gz: 0516ea9c4d4b019309c7dca87a3016856bfe6e760d937a67b17b3382c61f34aae0d1e9aacd7a7d960ea14db63029cad4e1ec2e7d9dcc01d8b62471cacbaf2e65
|
data/.gitignore
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
## Specific to RubyMotion:
|
17
|
+
.dat*
|
18
|
+
.repl_history
|
19
|
+
build/
|
20
|
+
*.bridgesupport
|
21
|
+
build-iPhoneOS/
|
22
|
+
build-iPhoneSimulator/
|
23
|
+
|
24
|
+
## Specific to RubyMotion (use of CocoaPods):
|
25
|
+
#
|
26
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
27
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
28
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
29
|
+
#
|
30
|
+
# vendor/Pods/
|
31
|
+
|
32
|
+
## Documentation cache and generated files:
|
33
|
+
/.yardoc/
|
34
|
+
/_yardoc/
|
35
|
+
/doc/
|
36
|
+
/rdoc/
|
37
|
+
|
38
|
+
## Environment normalization:
|
39
|
+
/.bundle/
|
40
|
+
/vendor/bundle
|
41
|
+
/lib/bundler/man/
|
42
|
+
|
43
|
+
# for a library or gem, you might want to ignore these files since the code is
|
44
|
+
# intended to run in multiple environments; otherwise, check them in:
|
45
|
+
# Gemfile.lock
|
46
|
+
# .ruby-version
|
47
|
+
# .ruby-gemset
|
48
|
+
|
49
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
50
|
+
.rvmrc
|
51
|
+
|
52
|
+
/.idea/
|
53
|
+
|
54
|
+
# Avoid the merging of the build intermediates
|
55
|
+
/target/
|
56
|
+
|
57
|
+
/lib/altsv/native.bundle
|
data/.travis.yml
ADDED
data/Cargo.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
[[package]]
|
2
|
+
name = "altsv"
|
3
|
+
version = "0.0.1"
|
4
|
+
dependencies = [
|
5
|
+
"helix 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
6
|
+
"libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
7
|
+
]
|
8
|
+
|
9
|
+
[[package]]
|
10
|
+
name = "cstr-macro"
|
11
|
+
version = "0.1.0"
|
12
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
13
|
+
|
14
|
+
[[package]]
|
15
|
+
name = "helix"
|
16
|
+
version = "0.7.5"
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
18
|
+
dependencies = [
|
19
|
+
"cstr-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
20
|
+
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
21
|
+
"libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
22
|
+
]
|
23
|
+
|
24
|
+
[[package]]
|
25
|
+
name = "libc"
|
26
|
+
version = "0.2.48"
|
27
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
28
|
+
|
29
|
+
[[package]]
|
30
|
+
name = "libcruby-sys"
|
31
|
+
version = "0.7.5"
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
33
|
+
dependencies = [
|
34
|
+
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
35
|
+
]
|
36
|
+
|
37
|
+
[metadata]
|
38
|
+
"checksum cstr-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db53fddba18cdd35477a7213a3ef6acfbfa333c31b42ce019e544c4a1420a06f"
|
39
|
+
"checksum helix 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49a017e3e798ad9386e0a0584e66fd6c04a80ccc1242eb8f689c62ce6f408240"
|
40
|
+
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
41
|
+
"checksum libcruby-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fef6028cdce0c8d55676fd1d66bb810facef8cade0dd71d28511d375e84da4c0"
|
data/Cargo.toml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2019 condor
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# altsv
|
2
|
+
|
3
|
+
An ALTernative LTSV Parser / Dumper gem partially written in Rust.
|
4
|
+
|
5
|
+
## Prerequisites
|
6
|
+
|
7
|
+
* rustc (tested with 1.32.0)
|
8
|
+
* cargo (The de-fact standard package manager for Rust)
|
9
|
+
|
10
|
+
You can install both with [rustup.rs](https://rustup.rs/)
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'altsv'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install altsv
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
At first, you should require altsv:
|
31
|
+
|
32
|
+
require 'altsv'
|
33
|
+
|
34
|
+
In addition, if you manage gems with bundler, you should add the statement below into your Gemfile:
|
35
|
+
|
36
|
+
gem 'altsv'
|
37
|
+
|
38
|
+
|
39
|
+
### parsing LTSV
|
40
|
+
|
41
|
+
# parse string
|
42
|
+
string = "label1:value1\tlabel2:value2"
|
43
|
+
values = Altsv.parse(string) # => [{:label1 => "value1", :label2 => "value2"}]
|
44
|
+
|
45
|
+
# parse via stream
|
46
|
+
# content: as below
|
47
|
+
# label1_1:value1_1\tlabel1_2:value1_2
|
48
|
+
# label2_1:value2_1\tlabel2_2:value2_2
|
49
|
+
stream = File.open("some_file.ltsv", "r")
|
50
|
+
values = Altsv.parse(stream)
|
51
|
+
# => [{:label1_1 => "value1_2", :label1_2 => "value1_2"},
|
52
|
+
# {:label2_1 => "value2_2", :label2_2 => "value2_2"}]
|
53
|
+
|
54
|
+
### loading LTSV file
|
55
|
+
|
56
|
+
# load via path
|
57
|
+
values = Altsv.load("some_path.ltsv")
|
58
|
+
|
59
|
+
# load via stream
|
60
|
+
stream = File.open("some_file.ltsv", "r")
|
61
|
+
values = Altsv.load(stream) # => same as LTSV.parse(stream)
|
62
|
+
|
63
|
+
### dumping into LTSV
|
64
|
+
|
65
|
+
value = {label1: "value1", label2: "value2"}
|
66
|
+
dumped = Altsv.dump(value) # => "label1:value1\tlabel2:value2"
|
67
|
+
|
68
|
+
Dumped objects should respond to :to_hash.
|
69
|
+
|
70
|
+
## Development
|
71
|
+
|
72
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
73
|
+
|
74
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
75
|
+
|
76
|
+
## Contributing
|
77
|
+
|
78
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/condor/altsv.
|
data/Rakefile
ADDED
data/altsv.gemspec
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "altsv/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "altsv"
|
9
|
+
spec.version = Altsv::VERSION
|
10
|
+
spec.authors = ["condor"]
|
11
|
+
spec.email = ["condor1226@gmail.com"]
|
12
|
+
|
13
|
+
spec.summary = %q{an ALTernative ltSV parser / dumper library}
|
14
|
+
spec.description = %q{A more lightweight LTSV handler library partially written in Rust.}
|
15
|
+
spec.homepage = "https://github.com/condor/altsv"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/condor/altsv"
|
22
|
+
spec.metadata["changelog_uri"] = "https://github.com/condor/altsv/blob/master/CHANGELOG.md"
|
23
|
+
else
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
25
|
+
"public gem pushes."
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specify which files should be added to the gem when it is released.
|
29
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
31
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
32
|
+
end
|
33
|
+
spec.bindir = "exe"
|
34
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
spec.extensions = %w(ext/altsv/extconf.rb)
|
37
|
+
|
38
|
+
spec.add_dependency 'helix_runtime', '~> 0.7.5'
|
39
|
+
|
40
|
+
spec.add_development_dependency 'pry'
|
41
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
42
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
43
|
+
spec.add_development_dependency "rspec", "~> 3.8", '< 4.0'
|
44
|
+
spec.add_development_dependency 'ltsv'
|
45
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "altsv"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
RAKEFILE = 'native.rake'
|
2
|
+
|
3
|
+
def command_exists(command, *extra_test_arguments)
|
4
|
+
raise "Command #{command} not found. exitting..." unless system(command, *extra_test_arguments)
|
5
|
+
end
|
6
|
+
|
7
|
+
command_exists('cargo', '--version')
|
8
|
+
command_exists('rustc', '--version')
|
9
|
+
|
10
|
+
File.open('Makefile', 'w') do |makefile|
|
11
|
+
makefile.puts <<~MAKEFILE
|
12
|
+
clean:
|
13
|
+
\trake -f #{RAKEFILE} clobber
|
14
|
+
install:
|
15
|
+
\trake -f #{RAKEFILE} build
|
16
|
+
MAKEFILE
|
17
|
+
end
|
18
|
+
|
19
|
+
File.open(RAKEFILE, 'w') do |rakefile|
|
20
|
+
rakefile.puts <<~RUBY
|
21
|
+
require 'helix_runtime/build_task'
|
22
|
+
|
23
|
+
HelixRuntime::BuildTask.new
|
24
|
+
RUBY
|
25
|
+
end
|
data/lib/altsv.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'helix_runtime'
|
3
|
+
require 'altsv/native'
|
4
|
+
|
5
|
+
require "altsv/version"
|
6
|
+
|
7
|
+
class Altsv
|
8
|
+
class Error < StandardError; end
|
9
|
+
# Your code goes here...
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def parse(input)
|
13
|
+
case input
|
14
|
+
when String
|
15
|
+
parse_native input
|
16
|
+
when StringIO
|
17
|
+
parse_native input.string
|
18
|
+
when IO
|
19
|
+
input.each_line.map{|line| parse_line_native line}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def load(path_or_io)
|
24
|
+
case path_or_io
|
25
|
+
when String
|
26
|
+
File.open(path_or_io){|f| parse f}
|
27
|
+
when IO
|
28
|
+
parse(path_or_io)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "#{name}.#{__method__} only accepts IO or path."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def dump(value)
|
35
|
+
dump_native value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/src/lib.rs
ADDED
@@ -0,0 +1,356 @@
|
|
1
|
+
use std::collections::HashMap;
|
2
|
+
use std::ffi::CString;
|
3
|
+
|
4
|
+
use libcruby_sys::*;
|
5
|
+
|
6
|
+
use helix::*;
|
7
|
+
|
8
|
+
const BUFFER_SIZE_KEY: usize = 64usize;
|
9
|
+
const BUFFER_SIZE_VALUE: usize = 128usize;
|
10
|
+
|
11
|
+
struct HashMapWrapper {
|
12
|
+
map: HashMap<Symbol, Option<CString>>,
|
13
|
+
}
|
14
|
+
|
15
|
+
impl HashMapWrapper {
|
16
|
+
fn with_capacity(capacity: usize) -> Self {
|
17
|
+
HashMapWrapper {
|
18
|
+
map: HashMap::with_capacity(capacity)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
fn insert(&mut self, key: &String, value: &str) {
|
23
|
+
let value_to_insert: Option<CString>;
|
24
|
+
if value.is_empty() {
|
25
|
+
value_to_insert = None;
|
26
|
+
} else {
|
27
|
+
value_to_insert = Some(CString::new(value).expect("This must success since it is from String"))
|
28
|
+
}
|
29
|
+
let key_to_insert = Symbol::from_string(key.clone());
|
30
|
+
|
31
|
+
self.map.insert(key_to_insert, value_to_insert);
|
32
|
+
}
|
33
|
+
|
34
|
+
fn inner(self) -> HashMap<Symbol, Option<CString>> {
|
35
|
+
self.map
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
impl ToRuby for HashMapWrapper {
|
40
|
+
fn to_ruby(self) -> ToRubyResult {
|
41
|
+
let hash: VALUE;
|
42
|
+
unsafe {
|
43
|
+
hash = rb_hash_new();
|
44
|
+
|
45
|
+
for (key, value) in self.inner().into_iter() {
|
46
|
+
let key_ruby = key.to_ruby().expect("This must success since it is from String");
|
47
|
+
let value_ruby = match value {
|
48
|
+
None => Qnil,
|
49
|
+
Some(v) => rb_utf8_str_new(v.as_ptr(), v.as_bytes().len() as i64),
|
50
|
+
};
|
51
|
+
rb_hash_aset(hash, key_ruby, value_ruby);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
return Ok(hash);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
fn parse_line(input: &str) -> HashMapWrapper {
|
60
|
+
let mut key = String::with_capacity(BUFFER_SIZE_KEY);
|
61
|
+
let mut value = String::with_capacity(BUFFER_SIZE_VALUE);
|
62
|
+
let mut in_value = false;
|
63
|
+
let mut escaping = false;
|
64
|
+
|
65
|
+
// First: count the key-value pairs.
|
66
|
+
let mut count = 0usize;
|
67
|
+
let mut chars = input.chars();
|
68
|
+
loop {
|
69
|
+
match chars.next() {
|
70
|
+
None => break,
|
71
|
+
Some(c) => {
|
72
|
+
match c {
|
73
|
+
'\t' => count += 1,
|
74
|
+
_ => {}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
let mut line= HashMapWrapper::with_capacity(count);
|
81
|
+
|
82
|
+
// start actual parsing
|
83
|
+
let mut chars = input.chars();
|
84
|
+
loop {
|
85
|
+
match chars.next() {
|
86
|
+
None => {
|
87
|
+
if !key.is_empty() {
|
88
|
+
line.insert(&key, &value);
|
89
|
+
}
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
Some(c) => {
|
93
|
+
if c == '\r' || c == '\n' {
|
94
|
+
if !key.is_empty() {
|
95
|
+
line.insert(&key, &value);
|
96
|
+
}
|
97
|
+
break;
|
98
|
+
}
|
99
|
+
|
100
|
+
let current_char: char;
|
101
|
+
if escaping {
|
102
|
+
match c {
|
103
|
+
'n' => current_char = '\n',
|
104
|
+
'r' => current_char = '\r',
|
105
|
+
't' => current_char = '\t',
|
106
|
+
'\\' => current_char = '\\',
|
107
|
+
_ => {
|
108
|
+
current_char = c;
|
109
|
+
if in_value {
|
110
|
+
value.push('\\')
|
111
|
+
}else {
|
112
|
+
key.push('\\')
|
113
|
+
}
|
114
|
+
},
|
115
|
+
}
|
116
|
+
escaping = false;
|
117
|
+
} else {
|
118
|
+
match c {
|
119
|
+
'\\' => {
|
120
|
+
escaping = true;
|
121
|
+
continue;
|
122
|
+
},
|
123
|
+
':' => {
|
124
|
+
// key-value separator(only when not separated)
|
125
|
+
if !in_value {
|
126
|
+
in_value = true;
|
127
|
+
continue;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
'\t' => {
|
131
|
+
// field separator
|
132
|
+
if !key.is_empty() {
|
133
|
+
// TODO: Whether OR NOT the empty label should cause error?
|
134
|
+
line.insert(&key, &value);
|
135
|
+
}
|
136
|
+
key.clear();
|
137
|
+
value.clear();
|
138
|
+
in_value = false;
|
139
|
+
continue;
|
140
|
+
}
|
141
|
+
_ => {}
|
142
|
+
}
|
143
|
+
current_char = c;
|
144
|
+
}
|
145
|
+
|
146
|
+
if in_value {
|
147
|
+
value.push(current_char)
|
148
|
+
} else {
|
149
|
+
key.push(current_char)
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
line
|
156
|
+
}
|
157
|
+
|
158
|
+
const METHOD_NAME_RESPOND_TO: &[u8] = b"respond_to?\0`";
|
159
|
+
const METHOD_NAME_TO_H: &[u8] = b"to_h\0";
|
160
|
+
const METHOD_NAME_TO_HASH: &[u8] = b"to_hash\0";
|
161
|
+
const METHOD_NAME_TO_S: &[u8] = b"to_s\0";
|
162
|
+
|
163
|
+
const CLASS_NAME_ARGUMENT_ERROR: &str = "ArgumentError";
|
164
|
+
|
165
|
+
const MESSAGE_DUMP_ERROR: &'static str = "Argument does not respond to neither :to_h nor :to_hash";
|
166
|
+
|
167
|
+
unsafe fn rb_intern_u8(method_name: &[u8]) -> ID {
|
168
|
+
use std::mem;
|
169
|
+
rb_intern(mem::transmute::<&u8, c_string>(&method_name[0]))
|
170
|
+
}
|
171
|
+
|
172
|
+
unsafe fn respond_to(object: VALUE, name: ID) -> bool {
|
173
|
+
let respond_to = rb_intern_u8(METHOD_NAME_RESPOND_TO);
|
174
|
+
let method = rb_id2sym(name);
|
175
|
+
|
176
|
+
rb_funcall(object, respond_to, 1, method) == Qtrue
|
177
|
+
}
|
178
|
+
|
179
|
+
extern "C" fn join_hash(key: VALUE, value: VALUE, farg: *mut void) -> st_retval {
|
180
|
+
use std::mem;
|
181
|
+
|
182
|
+
let s = unsafe {mem::transmute::<*mut void, *mut DumpResult>(farg) };
|
183
|
+
|
184
|
+
match unsafe{ (*s).push_value(key, true) } {
|
185
|
+
None => {}
|
186
|
+
Some(error) => {
|
187
|
+
unsafe{ (*s).set_error(error) };
|
188
|
+
return st_retval::ST_STOP;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
unsafe { (*s).push_char(':') };
|
193
|
+
|
194
|
+
match unsafe{ (*s).push_value(value, false) } {
|
195
|
+
None => {}
|
196
|
+
Some(error) => {
|
197
|
+
unsafe{ (*s).set_error(error) };
|
198
|
+
return st_retval::ST_STOP;
|
199
|
+
}
|
200
|
+
}
|
201
|
+
unsafe { (*s).push_char('\t') };
|
202
|
+
|
203
|
+
st_retval::ST_CONTINUE
|
204
|
+
}
|
205
|
+
|
206
|
+
struct DumpResult {
|
207
|
+
error: Option<Error>,
|
208
|
+
buffer: String,
|
209
|
+
}
|
210
|
+
|
211
|
+
impl DumpResult {
|
212
|
+
fn new(item_count: usize) -> Self{
|
213
|
+
DumpResult {
|
214
|
+
buffer: String::with_capacity(item_count * (BUFFER_SIZE_KEY + BUFFER_SIZE_VALUE)),
|
215
|
+
error: None,
|
216
|
+
}
|
217
|
+
}
|
218
|
+
fn push_value(&mut self, v: VALUE, for_key: bool) -> Option<Error>{
|
219
|
+
match unsafe {
|
220
|
+
if RB_TYPE_P(v, T_SYMBOL) {
|
221
|
+
String::from_ruby(rb_id2str(rb_sym2id(v)))
|
222
|
+
} else if RB_TYPE_P(v, T_STRING) {
|
223
|
+
String::from_ruby(v)
|
224
|
+
} else {
|
225
|
+
let to_s = rb_intern_u8(&METHOD_NAME_TO_S);
|
226
|
+
String::from_ruby(rb_funcall(v, to_s, 0))
|
227
|
+
}
|
228
|
+
} {
|
229
|
+
Ok(checked) => {
|
230
|
+
use helix::Error;
|
231
|
+
|
232
|
+
for c in String::from_checked(checked).chars() {
|
233
|
+
match c {
|
234
|
+
'\r' => self.buffer.push_str("\\r"),
|
235
|
+
'\n' => self.buffer.push_str("\\n"),
|
236
|
+
'\t' => self.buffer.push_str("\\t"),
|
237
|
+
':' => {
|
238
|
+
if for_key {
|
239
|
+
return Some(Error::with_c_string(CString::new("Key should not include ':'").unwrap().as_ptr()));
|
240
|
+
}
|
241
|
+
self.buffer.push(c)
|
242
|
+
},
|
243
|
+
_ => self.buffer.push(c),
|
244
|
+
}
|
245
|
+
}
|
246
|
+
None
|
247
|
+
},
|
248
|
+
Err(e) => Some(e),
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
fn push_char(&mut self, c: char) {
|
253
|
+
self.buffer.push(c);
|
254
|
+
}
|
255
|
+
|
256
|
+
fn set_error(&mut self, e: Error) {
|
257
|
+
self.error = Some(e);
|
258
|
+
}
|
259
|
+
|
260
|
+
fn extract(self) -> Result<String, Error> {
|
261
|
+
match self.error {
|
262
|
+
Some(e) => Err(e),
|
263
|
+
None => Ok(String::from(self.buffer.trim_end())),
|
264
|
+
}
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
struct DumpArgument {
|
269
|
+
inner: VALUE,
|
270
|
+
count: usize,
|
271
|
+
}
|
272
|
+
|
273
|
+
impl DumpArgument {
|
274
|
+
pub unsafe fn from_hash(inner: VALUE) -> Self{
|
275
|
+
let count = RHASH_SIZE(inner) as usize;
|
276
|
+
|
277
|
+
DumpArgument{
|
278
|
+
inner,
|
279
|
+
count,
|
280
|
+
}
|
281
|
+
}
|
282
|
+
pub fn dump(&self) -> Result<String, Error> {
|
283
|
+
use std::mem;
|
284
|
+
|
285
|
+
let mut dump_result = DumpResult::new(self.count);
|
286
|
+
unsafe{
|
287
|
+
rb_hash_foreach(
|
288
|
+
self.inner,
|
289
|
+
join_hash,
|
290
|
+
mem::transmute::<&mut DumpResult, *mut void>(&mut dump_result)
|
291
|
+
)
|
292
|
+
};
|
293
|
+
|
294
|
+
dump_result.extract()
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
impl FromRuby for DumpArgument {
|
299
|
+
type Checked = CheckedValue<DumpArgument>;
|
300
|
+
|
301
|
+
fn from_ruby(value: VALUE) -> CheckResult<CheckedValue<DumpArgument>> {
|
302
|
+
unsafe {
|
303
|
+
if RB_TYPE_P(value, T_HASH) {
|
304
|
+
return Ok(CheckedValue::new(value));
|
305
|
+
}
|
306
|
+
|
307
|
+
let hashed_value: VALUE;
|
308
|
+
let to_h = rb_intern_u8(METHOD_NAME_TO_H);
|
309
|
+
if respond_to(value, to_h) {
|
310
|
+
hashed_value = rb_funcall(value, to_h, 0);
|
311
|
+
return Ok(CheckedValue::new(hashed_value));
|
312
|
+
}
|
313
|
+
|
314
|
+
let hashed_value: VALUE;
|
315
|
+
let to_h = rb_intern_u8(METHOD_NAME_TO_HASH);
|
316
|
+
if respond_to(value, to_h) {
|
317
|
+
hashed_value = rb_funcall(value, to_h, 0);
|
318
|
+
return Ok(CheckedValue::new(hashed_value));
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
let argument_error = unsafe {
|
323
|
+
let id_argument_error = rb_intern(CString::new(CLASS_NAME_ARGUMENT_ERROR).unwrap().as_ptr());
|
324
|
+
rb_const_get(rb_cObject, id_argument_error)
|
325
|
+
};
|
326
|
+
let argument_error_class = unsafe{ Class::from_value(argument_error) };
|
327
|
+
raise!(argument_error_class, MESSAGE_DUMP_ERROR);
|
328
|
+
}
|
329
|
+
|
330
|
+
fn from_checked(checked: CheckedValue<DumpArgument>) -> DumpArgument {
|
331
|
+
unsafe{ DumpArgument::from_hash(checked.to_value())}
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
ruby! {
|
336
|
+
class Altsv {
|
337
|
+
def parse_native(input: String) -> Vec<HashMapWrapper> {
|
338
|
+
let mut hashes: Vec<HashMapWrapper> = Vec::new();
|
339
|
+
let mut lines = input.lines();
|
340
|
+
loop {
|
341
|
+
match lines.next() {
|
342
|
+
Some(line) => hashes.push(parse_line(line)),
|
343
|
+
None => break,
|
344
|
+
}
|
345
|
+
}
|
346
|
+
hashes
|
347
|
+
}
|
348
|
+
def parse_line_native(input: String) -> HashMapWrapper {
|
349
|
+
parse_line(&input)
|
350
|
+
}
|
351
|
+
|
352
|
+
def dump_native(value: DumpArgument) -> Result<String, Error> {
|
353
|
+
value.dump()
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: altsv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- condor
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: helix_runtime
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.17'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.17'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.8'
|
76
|
+
- - "<"
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '4.0'
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - "~>"
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '3.8'
|
86
|
+
- - "<"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '4.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: ltsv
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
description: A more lightweight LTSV handler library partially written in Rust.
|
104
|
+
email:
|
105
|
+
- condor1226@gmail.com
|
106
|
+
executables: []
|
107
|
+
extensions:
|
108
|
+
- ext/altsv/extconf.rb
|
109
|
+
extra_rdoc_files: []
|
110
|
+
files:
|
111
|
+
- ".gitignore"
|
112
|
+
- ".travis.yml"
|
113
|
+
- Cargo.lock
|
114
|
+
- Cargo.toml
|
115
|
+
- Gemfile
|
116
|
+
- LICENSE
|
117
|
+
- README.md
|
118
|
+
- Rakefile
|
119
|
+
- altsv.gemspec
|
120
|
+
- bin/console
|
121
|
+
- bin/setup
|
122
|
+
- ext/altsv/extconf.rb
|
123
|
+
- lib/altsv.rb
|
124
|
+
- lib/altsv/version.rb
|
125
|
+
- src/lib.rs
|
126
|
+
homepage: https://github.com/condor/altsv
|
127
|
+
licenses: []
|
128
|
+
metadata:
|
129
|
+
homepage_uri: https://github.com/condor/altsv
|
130
|
+
source_code_uri: https://github.com/condor/altsv
|
131
|
+
changelog_uri: https://github.com/condor/altsv/blob/master/CHANGELOG.md
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubygems_version: 3.0.3
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: an ALTernative ltSV parser / dumper library
|
151
|
+
test_files: []
|