ruby-fst 0.2.0-aarch64-linux-musl
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/.rubocop.yml +82 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +46 -0
- data/README.md +174 -0
- data/Rakefile +79 -0
- data/lib/ruby_fst/3.2/ruby_fst.so +0 -0
- data/lib/ruby_fst/3.3/ruby_fst.so +0 -0
- data/lib/ruby_fst/3.4/ruby_fst.so +0 -0
- data/lib/ruby_fst/version.rb +5 -0
- data/lib/ruby_fst.rb +40 -0
- data/ruby_fst.gemspec +27 -0
- metadata +62 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 52c0b414ed7da3d72f12d4e5f74e58664be57700695340ea6f528f22d0759765
|
|
4
|
+
data.tar.gz: f2b86e019823d8308d871ea921200ec326e5892842201b93ae4c25772c85b097
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 40064639c4a1cd523c437973b714fef20740a53483c834f2f93249fbf0a4f62e7eb7e5625b8c89ab6fe2f1b7a4341a86eb69b24598d6e65eaf3f6ed55cf47a45
|
|
7
|
+
data.tar.gz: 44a54e7b72e2978f31ee73dc6bcad18e9f184808a7953d61d09efdd2cb6257e71b57912b4248aa262bce90db887d391ccb5e4e28193fb43e5b86921ced027570
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-minitest
|
|
3
|
+
- rubocop-rake
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
NewCops: enable
|
|
7
|
+
TargetRubyVersion: 3.2
|
|
8
|
+
SuggestExtensions: false
|
|
9
|
+
Exclude:
|
|
10
|
+
- 'target/**/*'
|
|
11
|
+
- 'tmp/**/*'
|
|
12
|
+
- 'pkg/**/*'
|
|
13
|
+
- 'vendor/**/*'
|
|
14
|
+
|
|
15
|
+
Style/StringLiterals:
|
|
16
|
+
EnforcedStyle: single_quotes
|
|
17
|
+
|
|
18
|
+
Style/StringLiteralsInInterpolation:
|
|
19
|
+
EnforcedStyle: single_quotes
|
|
20
|
+
|
|
21
|
+
Style/SymbolArray:
|
|
22
|
+
EnforcedStyle: percent
|
|
23
|
+
MinSize: 3
|
|
24
|
+
|
|
25
|
+
Style/WordArray:
|
|
26
|
+
EnforcedStyle: percent
|
|
27
|
+
MinSize: 3
|
|
28
|
+
|
|
29
|
+
Style/PercentLiteralDelimiters:
|
|
30
|
+
PreferredDelimiters:
|
|
31
|
+
default: '()'
|
|
32
|
+
'%i': '()'
|
|
33
|
+
'%w': '()'
|
|
34
|
+
|
|
35
|
+
Style/Documentation:
|
|
36
|
+
Enabled: false
|
|
37
|
+
|
|
38
|
+
Style/HashEachMethods:
|
|
39
|
+
Exclude:
|
|
40
|
+
- 'test/**/*'
|
|
41
|
+
|
|
42
|
+
Style/MapIntoArray:
|
|
43
|
+
Exclude:
|
|
44
|
+
- 'test/**/*'
|
|
45
|
+
|
|
46
|
+
Naming/MethodParameterName:
|
|
47
|
+
AllowedNames:
|
|
48
|
+
- ge
|
|
49
|
+
- le
|
|
50
|
+
- id
|
|
51
|
+
- to
|
|
52
|
+
- by
|
|
53
|
+
- on
|
|
54
|
+
- in
|
|
55
|
+
- at
|
|
56
|
+
- of
|
|
57
|
+
- or
|
|
58
|
+
- if
|
|
59
|
+
- is
|
|
60
|
+
- as
|
|
61
|
+
- it
|
|
62
|
+
|
|
63
|
+
Naming/VariableNumber:
|
|
64
|
+
CheckMethodNames: false
|
|
65
|
+
|
|
66
|
+
Layout/LineLength:
|
|
67
|
+
Max: 120
|
|
68
|
+
|
|
69
|
+
Metrics/BlockLength:
|
|
70
|
+
Exclude:
|
|
71
|
+
- 'test/**/*'
|
|
72
|
+
- '*.gemspec'
|
|
73
|
+
- 'Rakefile'
|
|
74
|
+
|
|
75
|
+
Metrics/MethodLength:
|
|
76
|
+
Max: 25
|
|
77
|
+
|
|
78
|
+
Metrics/AbcSize:
|
|
79
|
+
Max: 25
|
|
80
|
+
|
|
81
|
+
Minitest/MultipleAssertions:
|
|
82
|
+
Max: 8
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] — 2026-05-13
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `Map.from_path_mmap` and `Set.from_path_mmap` for memory-mapped loading of large FSTs.
|
|
13
|
+
- `Map#range(ge:, le:)` and `Set#range(ge:, le:)` streaming range iteration.
|
|
14
|
+
- `Map#starts_with(prefix)` and `Set#starts_with(prefix)` prefix scans.
|
|
15
|
+
- `Map#get_le_value` and `Map#get_ge_value` — return only the value (no key allocation) for floor/ceiling lookups.
|
|
16
|
+
- Precompiled native gems for Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (x64) — installs without a Rust toolchain on those platforms.
|
|
17
|
+
- Tests covering binary string encoding, key lifetime after stream drop, builder GC, and 0xFF prefix-scan edge case.
|
|
18
|
+
- RuboCop lint configuration with rubocop-minitest and rubocop-rake plugins; runs as part of `rake` and as a CI gate.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Minimum Ruby version raised to 3.2.
|
|
22
|
+
- `MapBuilder` and `SetBuilder` now operate on a generic `Storage` backing (heap or mmap) for both `Map` and `Set`.
|
|
23
|
+
- Upgraded to magnus 0.8 (drops support for Ruby 2.7 and 3.0 in the underlying bindings; we already require 3.2+).
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
- README clarifies key encoding, insertion order, mmap vs in-memory loading, and Levenshtein UTF-8 requirement.
|
|
27
|
+
|
|
28
|
+
## [0.1.0] — 2026-05-13
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Initial release: `RubyFst::Map`, `RubyFst::Set`, `MapBuilder`, `SetBuilder`.
|
|
32
|
+
- Floor/ceiling lookups (`get_le`, `get_ge`).
|
|
33
|
+
- Levenshtein automaton search.
|
|
34
|
+
- Bytes/file serialization via `to_bytes` / `save` / `from_path`.
|
data/Gemfile
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gemspec
|
|
6
|
+
|
|
7
|
+
gem 'minitest', '~> 5.0'
|
|
8
|
+
gem 'rake', '~> 13.0'
|
|
9
|
+
gem 'rake-compiler', '~> 1.2'
|
|
10
|
+
gem 'rubocop', '~> 1.60', require: false
|
|
11
|
+
gem 'rubocop-minitest', '~> 0.34', require: false
|
|
12
|
+
gem 'rubocop-rake', '~> 0.6', require: false
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Denis Sablic
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This gem wraps the fst crate (https://github.com/BurntSushi/fst) by
|
|
26
|
+
Andrew Gallant, which is licensed under the MIT License:
|
|
27
|
+
|
|
28
|
+
Copyright (c) 2015 Andrew Gallant
|
|
29
|
+
|
|
30
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
31
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
32
|
+
in the Software without restriction, including without limitation the rights
|
|
33
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
34
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
35
|
+
furnished to do so, subject to the following conditions:
|
|
36
|
+
|
|
37
|
+
The above copyright notice and this permission notice shall be included in
|
|
38
|
+
all copies or substantial portions of the Software.
|
|
39
|
+
|
|
40
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
41
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
42
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
43
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
44
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
45
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
46
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# ruby-fst
|
|
2
|
+
|
|
3
|
+
[](https://github.com/dsablic/ruby-fst/actions/workflows/ci.yml) [](https://badge.fury.io/rb/ruby-fst) [](/LICENSE.txt) [](https://www.ruby-lang.org/)
|
|
4
|
+
|
|
5
|
+
Ruby bindings for the [fst](https://github.com/BurntSushi/fst) crate by Andrew Gallant. Provides finite state transducer backed ordered maps and sets with fast lookup, prefix and range scans, floor/ceiling lookups, and Levenshtein fuzzy search.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Ruby >= 3.2
|
|
10
|
+
- For source installs: a Rust toolchain. Precompiled gems are published for Linux (x86_64, aarch64, glibc and musl), macOS (x86_64, arm64), and Windows (x64) — these install with no Rust toolchain required.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
gem install ruby-fst
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or add to your Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem 'ruby-fst'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Keys are byte strings
|
|
25
|
+
|
|
26
|
+
All FST keys are arbitrary byte strings (`Encoding::BINARY`). When a key is returned from the gem (e.g. via `each`, `get_le`, `range`) it always carries the `Encoding::BINARY` encoding — be careful when interpolating into UTF-8 strings or `puts`-ing keys that contain non-ASCII bytes.
|
|
27
|
+
|
|
28
|
+
Keys must be inserted into a builder in **strictly ascending lexicographic byte order**, with no duplicates. Out-of-order or duplicate inserts raise.
|
|
29
|
+
|
|
30
|
+
## Map
|
|
31
|
+
|
|
32
|
+
Ordered map from byte string keys to unsigned 64-bit integer values.
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
require 'ruby_fst'
|
|
36
|
+
|
|
37
|
+
builder = RubyFst::MapBuilder.new
|
|
38
|
+
builder.insert('bar', 2)
|
|
39
|
+
builder.insert('baz', 3)
|
|
40
|
+
builder.insert('foo', 1)
|
|
41
|
+
map = RubyFst::Map.new(builder.finish)
|
|
42
|
+
|
|
43
|
+
map['foo'] # => 1
|
|
44
|
+
map.get('missing') # => nil
|
|
45
|
+
map.contains?('bar') # => true
|
|
46
|
+
map.length # => 3
|
|
47
|
+
|
|
48
|
+
map.each { |key, value| puts("#{key}: #{value}") }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Set
|
|
52
|
+
|
|
53
|
+
Ordered set of byte string keys.
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
builder = RubyFst::SetBuilder.new
|
|
57
|
+
builder.insert('bar')
|
|
58
|
+
builder.insert('baz')
|
|
59
|
+
builder.insert('foo')
|
|
60
|
+
set = RubyFst::Set.new(builder.finish)
|
|
61
|
+
|
|
62
|
+
set.contains?('foo') # => true
|
|
63
|
+
set.length # => 3
|
|
64
|
+
|
|
65
|
+
set.each { |key| puts(key) }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Range queries
|
|
69
|
+
|
|
70
|
+
`Map#range` and `Set#range` stream every entry whose key falls in `[ge, le]`. Either bound may be omitted.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
map.range(ge: 'b', le: 'f') { |key, value| puts("#{key} -> #{value}") }
|
|
74
|
+
map.range(ge: 'b').to_a # Enumerator without a block
|
|
75
|
+
set.range(le: 'baz') { |key| puts(key) }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`Map#starts_with` and `Set#starts_with` stream every entry whose key begins with the given prefix.
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
map.starts_with('foo') { |key, value| puts("#{key} -> #{value}") }
|
|
82
|
+
set.starts_with('app').to_a
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Floor and ceiling lookups
|
|
86
|
+
|
|
87
|
+
`get_le` returns the greatest key less than or equal to the query. `get_ge` returns the smallest key greater than or equal to the query. Both return `[key, value]` or `nil`.
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
builder = RubyFst::MapBuilder.new
|
|
91
|
+
builder.insert('bar', 1)
|
|
92
|
+
builder.insert('foo', 2)
|
|
93
|
+
builder.insert('qux', 3)
|
|
94
|
+
map = RubyFst::Map.new(builder.finish)
|
|
95
|
+
|
|
96
|
+
map.get_le('dog') # => ["bar", 1]
|
|
97
|
+
map.get_ge('dog') # => ["foo", 2]
|
|
98
|
+
map.get_le('aaa') # => nil
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For range lookups where you only need the value, `get_le_value` and `get_ge_value` skip key reconstruction and return only the `Integer` (or `nil`):
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
map.get_le_value('dog') # => 1
|
|
105
|
+
map.get_ge_value('dog') # => 2
|
|
106
|
+
map.get_le_value('aaa') # => nil
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This is useful for IP range lookups. Encode range starts as 4-byte big-endian keys and use `get_le_value` to find which range an IP falls into:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
require 'ipaddr'
|
|
113
|
+
|
|
114
|
+
builder = RubyFst::MapBuilder.new
|
|
115
|
+
builder.insert([167_772_160].pack('N'), 1) # 10.0.0.0
|
|
116
|
+
builder.insert([3_232_235_520].pack('N'), 2) # 192.168.0.0
|
|
117
|
+
map = RubyFst::Map.new(builder.finish)
|
|
118
|
+
|
|
119
|
+
ip = IPAddr.new('10.0.0.100').to_i
|
|
120
|
+
label_id = map.get_le_value([ip].pack('N'))
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Levenshtein search
|
|
124
|
+
|
|
125
|
+
Find all keys within a given edit distance. The search runs as an automaton intersection with the FST, visiting only reachable states.
|
|
126
|
+
|
|
127
|
+
The query must be valid UTF-8 (the Levenshtein automaton is defined over Unicode codepoints), but the keys themselves can be any bytes — the automaton matches against the UTF-8 interpretation of the keys.
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
builder = RubyFst::MapBuilder.new
|
|
131
|
+
%w(bar baz cat foo fun).each_with_index { |w, i| builder.insert(w, i) }
|
|
132
|
+
map = RubyFst::Map.new(builder.finish)
|
|
133
|
+
|
|
134
|
+
map.search_levenshtein('far', 1) { |key, value| puts(key) }
|
|
135
|
+
# => bar
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Works on sets too:
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
set.search_levenshtein('university', 2) { |key| puts(key) }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Serialization
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
# Bytes
|
|
148
|
+
bytes = map.to_bytes # binary string
|
|
149
|
+
map = RubyFst::Map.new(bytes)
|
|
150
|
+
|
|
151
|
+
# File: read entire file into memory (good for small/medium FSTs)
|
|
152
|
+
map.save('/path/to/file.fst')
|
|
153
|
+
map = RubyFst::Map.from_path('/path/to/file.fst')
|
|
154
|
+
|
|
155
|
+
# File: memory-map the file (good for large FSTs; lookups page in only what they touch)
|
|
156
|
+
map = RubyFst::Map.from_path_mmap('/path/to/file.fst')
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
When using `from_path_mmap`, the file must remain unchanged on disk for the lifetime of the resulting `Map` or `Set` — modifying or truncating it causes undefined behavior.
|
|
160
|
+
|
|
161
|
+
## Development
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
bundle install
|
|
165
|
+
bundle exec rake # compile + test + rubocop
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Individual tasks: `rake compile`, `rake test`, `rake rubocop`. Bump the version (and CHANGELOG) with `rake bump[patch]` / `rake bump[minor]` / `rake bump[major]`. Tagging `vX.Y.Z` on GitHub triggers cross-compile and publish to RubyGems.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT. See [LICENSE.txt](LICENSE.txt) for details.
|
|
173
|
+
|
|
174
|
+
This gem wraps the [fst](https://github.com/BurntSushi/fst) crate by Andrew Gallant, also MIT licensed.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'rb_sys/extensiontask'
|
|
5
|
+
require 'rubocop/rake_task'
|
|
6
|
+
|
|
7
|
+
GEMSPEC = Gem::Specification.load('ruby_fst.gemspec')
|
|
8
|
+
|
|
9
|
+
RbSys::ExtensionTask.new('ruby_fst', GEMSPEC) do |ext|
|
|
10
|
+
ext.lib_dir = 'lib/ruby_fst'
|
|
11
|
+
ext.cross_compile = true
|
|
12
|
+
ext.cross_platform = %w(
|
|
13
|
+
aarch64-linux
|
|
14
|
+
aarch64-linux-musl
|
|
15
|
+
arm64-darwin
|
|
16
|
+
x64-mingw-ucrt
|
|
17
|
+
x86_64-darwin
|
|
18
|
+
x86_64-linux
|
|
19
|
+
x86_64-linux-musl
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Rake::TestTask.new do |t|
|
|
24
|
+
t.libs << 'test'
|
|
25
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
RuboCop::RakeTask.new
|
|
29
|
+
|
|
30
|
+
task default: %i(compile test rubocop)
|
|
31
|
+
|
|
32
|
+
desc 'Bump version (rake bump[patch], rake bump[minor], rake bump[major])'
|
|
33
|
+
task :bump, [:level] do |_, args|
|
|
34
|
+
level = args[:level] || 'patch'
|
|
35
|
+
version_file = File.join(__dir__, 'lib', 'ruby_fst', 'version.rb')
|
|
36
|
+
cargo_file = File.join(__dir__, 'ext', 'ruby_fst', 'Cargo.toml')
|
|
37
|
+
lock_file = File.join(__dir__, 'Cargo.lock')
|
|
38
|
+
|
|
39
|
+
content = File.read(version_file)
|
|
40
|
+
current = content[/VERSION = '(.+)'/, 1]
|
|
41
|
+
major, minor, patch = current.split('.').map(&:to_i)
|
|
42
|
+
|
|
43
|
+
new_version =
|
|
44
|
+
case level
|
|
45
|
+
when 'major' then "#{major + 1}.0.0"
|
|
46
|
+
when 'minor' then "#{major}.#{minor + 1}.0"
|
|
47
|
+
when 'patch' then "#{major}.#{minor}.#{patch + 1}"
|
|
48
|
+
else abort("Unknown level: #{level}. Use major, minor, or patch.")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
File.write(version_file, content.sub(/VERSION = '.+'/, "VERSION = '#{new_version}'"))
|
|
52
|
+
|
|
53
|
+
cargo = File.read(cargo_file)
|
|
54
|
+
File.write(cargo_file, cargo.sub(/^version = ".+"/, "version = \"#{new_version}\""))
|
|
55
|
+
|
|
56
|
+
if File.exist?(lock_file)
|
|
57
|
+
lock = File.read(lock_file)
|
|
58
|
+
File.write(
|
|
59
|
+
lock_file,
|
|
60
|
+
lock.sub(
|
|
61
|
+
/(\[\[package\]\]\nname = "ruby_fst"\nversion = ").+(")/,
|
|
62
|
+
"\\1#{new_version}\\2"
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
changelog_file = File.join(__dir__, 'CHANGELOG.md')
|
|
68
|
+
if File.exist?(changelog_file)
|
|
69
|
+
changelog = File.read(changelog_file)
|
|
70
|
+
today = Time.now.utc.strftime('%Y-%m-%d')
|
|
71
|
+
promoted = changelog.sub(
|
|
72
|
+
'## [Unreleased]',
|
|
73
|
+
"## [Unreleased]\n\n## [#{new_version}] — #{today}"
|
|
74
|
+
)
|
|
75
|
+
File.write(changelog_file, promoted) if promoted != changelog
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
puts("#{current} -> #{new_version}")
|
|
79
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/ruby_fst.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'ruby_fst/version'
|
|
4
|
+
require_relative 'ruby_fst/ruby_fst'
|
|
5
|
+
|
|
6
|
+
module RubyFst
|
|
7
|
+
module RangeQuery
|
|
8
|
+
def range(ge: nil, le: nil, &block)
|
|
9
|
+
return enum_for(:range, ge:, le:) unless block
|
|
10
|
+
|
|
11
|
+
_range(ge, le, &block)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def starts_with(prefix, &block)
|
|
15
|
+
return enum_for(:starts_with, prefix) unless block
|
|
16
|
+
|
|
17
|
+
_starts_with(prefix, &block)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Map
|
|
22
|
+
include Enumerable
|
|
23
|
+
|
|
24
|
+
alias _range range
|
|
25
|
+
alias _starts_with starts_with
|
|
26
|
+
private :_range, :_starts_with
|
|
27
|
+
|
|
28
|
+
prepend RangeQuery
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Set
|
|
32
|
+
include Enumerable
|
|
33
|
+
|
|
34
|
+
alias _range range
|
|
35
|
+
alias _starts_with starts_with
|
|
36
|
+
private :_range, :_starts_with
|
|
37
|
+
|
|
38
|
+
prepend RangeQuery
|
|
39
|
+
end
|
|
40
|
+
end
|
data/ruby_fst.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/ruby_fst/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'ruby-fst'
|
|
7
|
+
spec.version = RubyFst::VERSION
|
|
8
|
+
spec.authors = ['Denis Sablic']
|
|
9
|
+
spec.email = ['denis.sablic@gmail.com']
|
|
10
|
+
spec.summary = 'Ruby bindings for the Rust fst crate'
|
|
11
|
+
spec.description = 'Finite state transducer backed ordered sets and maps via the Rust fst crate by BurntSushi'
|
|
12
|
+
spec.homepage = 'https://github.com/dsablic/ruby-fst'
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
spec.required_ruby_version = '>= 3.2'
|
|
15
|
+
|
|
16
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/dsablic/ruby-fst'
|
|
18
|
+
spec.metadata['changelog_uri'] = 'https://github.com/dsablic/ruby-fst/blob/main/CHANGELOG.md'
|
|
19
|
+
|
|
20
|
+
spec.files = Dir.chdir(__dir__) do
|
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.start_with?('test/', '.git') }
|
|
22
|
+
end
|
|
23
|
+
spec.extensions = ['ext/ruby_fst/extconf.rb']
|
|
24
|
+
spec.require_paths = ['lib']
|
|
25
|
+
|
|
26
|
+
spec.add_dependency('rb_sys', '~> 0.9')
|
|
27
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby-fst
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: aarch64-linux-musl
|
|
6
|
+
authors:
|
|
7
|
+
- Denis Sablic
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-13 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Finite state transducer backed ordered sets and maps via the Rust fst
|
|
14
|
+
crate by BurntSushi
|
|
15
|
+
email:
|
|
16
|
+
- denis.sablic@gmail.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- ".rubocop.yml"
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- Gemfile
|
|
24
|
+
- LICENSE.txt
|
|
25
|
+
- README.md
|
|
26
|
+
- Rakefile
|
|
27
|
+
- lib/ruby_fst.rb
|
|
28
|
+
- lib/ruby_fst/3.2/ruby_fst.so
|
|
29
|
+
- lib/ruby_fst/3.3/ruby_fst.so
|
|
30
|
+
- lib/ruby_fst/3.4/ruby_fst.so
|
|
31
|
+
- lib/ruby_fst/version.rb
|
|
32
|
+
- ruby_fst.gemspec
|
|
33
|
+
homepage: https://github.com/dsablic/ruby-fst
|
|
34
|
+
licenses:
|
|
35
|
+
- MIT
|
|
36
|
+
metadata:
|
|
37
|
+
rubygems_mfa_required: 'true'
|
|
38
|
+
source_code_uri: https://github.com/dsablic/ruby-fst
|
|
39
|
+
changelog_uri: https://github.com/dsablic/ruby-fst/blob/main/CHANGELOG.md
|
|
40
|
+
post_install_message:
|
|
41
|
+
rdoc_options: []
|
|
42
|
+
require_paths:
|
|
43
|
+
- lib
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '3.2'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 3.5.dev
|
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: 3.3.22
|
|
57
|
+
requirements: []
|
|
58
|
+
rubygems_version: 3.5.23
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: Ruby bindings for the Rust fst crate
|
|
62
|
+
test_files: []
|