htslib 0.2.6 → 0.2.9

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: 35551ff5a5cd81937063363c243bc2ac3d12ce09ec69697cd0eedd93945526bd
4
- data.tar.gz: ec16e4f3cee66c50a582c4580d6cf266c0f349f8f6bc985864eda3d41b41e412
3
+ metadata.gz: c98f3937d65f091e9e834060f3a94578034d1bf55e7bc372e5ed388e618a6da4
4
+ data.tar.gz: 8e180044fef210935695bc60c0352b747b570b705f3ce9318b7538d19ddaf645
5
5
  SHA512:
6
- metadata.gz: f534244d9b6dfd8a741cf21f639205207df612a3107ce563640db7dadd591c140dba92cf6eaa68ae1a440ab9b958697d0a04fac8217ff0ca7dcea796557fe30b
7
- data.tar.gz: ac305efbbdd989f8f44e691499a0aa978aa4ed17e24546fd48826a25c2591a8f70a8ec0411456e123248597c518e4c41dc057fbd6738be49b96fc5198ba643cd
6
+ metadata.gz: 5324913bdbe97580fee1e54b6268f22aed2a0b84d449c8a4f62529981c82a57d6ae5cea7cc77ff1be7332425eea259bfe5daa1107b6144e36ef40bb4997dd28f
7
+ data.tar.gz: 7b230700951223aeda09d64fac7cca44d734d54ec03a7836c5a0fb036e552854db80b9ecaf57aa478e317c684e4712f2f03244e02474fd2ea53a4b93346d1371
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ruby-htslib
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/htslib.svg)](https://badge.fury.io/rb/htslib)
4
- ![CI](https://github.com/kojix2/ruby-htslib/workflows/CI/badge.svg)
4
+ [![test](https://github.com/kojix2/ruby-htslib/actions/workflows/ci.yml/badge.svg)](https://github.com/kojix2/ruby-htslib/actions/workflows/ci.yml)
5
5
  [![The MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
6
6
  [![DOI](https://zenodo.org/badge/247078205.svg)](https://zenodo.org/badge/latestdoi/247078205)
7
7
  [![Docs Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://rubydoc.info/gems/htslib)
@@ -17,7 +17,7 @@ Ruby-htslib is the [Ruby](https://www.ruby-lang.org) bindings to [HTSlib](https:
17
17
  - Ubuntu : `apt install libhts-dev`
18
18
  - macOS : `brew install htslib`
19
19
  - Windows : [mingw-w64-htslib](https://packages.msys2.org/base/mingw-w64-htslib) is automatically fetched when installing the gem ([RubyInstaller](https://rubyinstaller.org) only).
20
- - Build from source code (see Development section)
20
+ - Build from source code (see the Development section)
21
21
 
22
22
  ## Installation
23
23
 
@@ -30,10 +30,10 @@ will automatically detect the location of the shared library. If pkg-config does
30
30
  Alternatively, you can specify the directory of the shared library by setting the environment variable `HTSLIBDIR`.
31
31
 
32
32
  ```sh
33
- export HTSLIBDIR="/your/path/to/htslib" # libhts.so
33
+ export HTSLIBDIR="/your/path/to/htslib" # Directory where libhts.so is located
34
34
  ```
35
35
 
36
- ruby-htslib also works on Windows; if you use RubyInstaller, htslib will be prepared automatically.
36
+ ruby-htslib also works on Windows. If you use RubyInstaller, htslib will be prepared automatically.
37
37
 
38
38
  ## Usage
39
39
 
@@ -64,12 +64,24 @@ end
64
64
  bam.close
65
65
  ```
66
66
 
67
+ With a block
68
+
69
+ ```ruby
70
+ HTS::Bam.open("test/fixtures/moo.bam") do |bam|
71
+ bam.each do |r|
72
+ puts r.to_s
73
+ end
74
+ end
75
+ ```
76
+
67
77
  ### HTS::Bcf - VCF / BCF - Variant Call Format file
68
78
 
69
79
  Reading fields
70
80
 
71
81
  ```ruby
72
- bcf = HTS::Bcf.open("b.bcf")
82
+ require 'htslib'
83
+
84
+ bcf = HTS::Bcf.open("test/fixtures/test.bcf")
73
85
 
74
86
  bcf.each do |r|
75
87
  p chrom: r.chrom,
@@ -86,27 +98,38 @@ end
86
98
  bcf.close
87
99
  ```
88
100
 
101
+ With a block
102
+
103
+ ```ruby
104
+ HTS::Bcf.open("test/fixtures/test.bcf") do |bcf|
105
+ bcf.each do |r|
106
+ puts r.to_s
107
+ end
108
+ end
109
+ ```
110
+
89
111
  ### HTS::Faidx - FASTA / FASTQ - Nucleic acid sequence
90
112
 
91
113
  ```ruby
92
- fa = HTS::Faidx.open("c.fa")
93
- fa.seq("chr1:1-10")
114
+ fa = HTS::Faidx.open("test/fixtures/moo.fa")
115
+ fa.seq("chr1:1-10") # => CGCAACCCGA # 1-based
116
+ fa.close
94
117
  ```
95
118
 
96
- ### HTS::Tbx - Tabix - gff, bed, sam, vcf
119
+ ### HTS::Tabix - GFF / BED - TAB-delimited genome position file
97
120
 
98
121
  ```ruby
99
- tb = HTS::Tbx.open("test.vcf.gz")
100
- tb.query("chr1", 10000, 20000) do |line|
101
- p line
122
+ tb = HTS::Tabix.open("test/fixtures/test.vcf.gz")
123
+ tb.query("poo", 2000, 3000) do |line|
124
+ puts line.join("\t")
102
125
  end
126
+ tb.close
103
127
  ```
104
128
 
105
- Note: Faidx or Tbx should not be explicitly closed. See [#27](https://github.com/kojix2/ruby-htslib/issues/27)
106
-
107
129
  ### Low-level API
108
130
 
109
- `HTS::LibHTS` provides native C functions.
131
+ Middle architectural layer between high-level Ruby code and low-level C code.
132
+ `HTS::LibHTS` provides native C functions using [Ruby-FFI](https://github.com/ffi/ffi).
110
133
 
111
134
  ```ruby
112
135
  require 'htslib'
@@ -117,26 +140,31 @@ p b[:category]
117
140
  p b[:format]
118
141
  ```
119
142
 
120
- Macro functions
143
+ The low-level API makes it possible to perform detailed operations, such as calling CRAM-specific functions.
121
144
 
122
- htslib has a lot of macro functions for speed. Ruby-FFI cannot call C macro functions. However, essential functions are reimplemented in Ruby, and you can call them.
145
+ #### Macro functions
123
146
 
124
- Structs
147
+ HTSlib is designed to improve performance with many macro functions. However, it is not possible to call C macro functions directly from Ruby-FFI. To overcome this, important macro functions have been re-implemented in Ruby, allowing them to be called in the same way as native functions.
125
148
 
126
- Only a small number of C structs are implemented with FFI's ManagedStruct, which frees memory when Ruby's garbage collection fires. Other structs will need to be freed manually.
149
+ #### Garbage Collection and Memory Freeing
150
+
151
+ A small number of commonly used structs, such as `Bam1` and `Bcf1`, are implemented using FFI's `ManagedStruct`. This allows for automatic memory release when Ruby's garbage collection is triggered. On the other hand, other structs are implemented using `FFI::Struct`, and they will require manual memory release.
127
152
 
128
153
  ### Need more speed?
129
154
 
130
- Try Crystal. [HTS.cr](https://github.com/bio-cr/hts.cr) is implemented in Crystal language and provides an API compatible with ruby-htslib. Crystal language is not as flexible as Ruby language. You can not use `eval` methods and must always be careful with the types. Writing one-time scripts in Crystal may be less fun. However, if you have a clear idea of what you want to do in your mind, have already written code in Ruby, and need to run them over and over, try creating a command line tool in Crystal. The Crystal language is as fast as the Rust and C languages. It will give you incredible power to make tools.
155
+ Try Crystal. [HTS.cr](https://github.com/bio-cr/hts.cr) is implemented in Crystal language and provides an API compatible with ruby-htslib.
131
156
 
132
157
  ## Documentation
133
158
 
134
159
  - [TUTORIAL.md](TUTORIAL.md)
135
160
  - [API Documentation (develop branch)](https://kojix2.github.io/ruby-htslib/)
136
- - [RubyDoc.info - HTSlib](https://rdoc.info/gems/htslib)
161
+ - [API Documentation (released gem)](https://rubydoc.info/gems/htslib)
137
162
 
138
163
  ## Development
139
164
 
165
+ #### Compile from source code
166
+
167
+ [GNU Autotools](https://en.wikipedia.org/wiki/GNU_Autotools) is required to compile htslib.
140
168
  To get started with development:
141
169
 
142
170
  ```sh
@@ -147,21 +175,41 @@ bundle exec rake htslib:build
147
175
  bundle exec rake test
148
176
  ```
149
177
 
150
- [GNU Autotools](https://en.wikipedia.org/wiki/GNU_Autotools) is required to compile htslib.
178
+ #### Macro functions are reimplemented
151
179
 
152
180
  HTSlib has many macro functions. These macro functions cannot be called from FFI and must be reimplemented in Ruby.
153
181
 
154
- - Use the new version of Ruby to take full advantage of Ruby's potential.
155
- - This is possible because we have a small number of users.
156
- - Remain compatible with [HTS.cr](https://github.com/bio-cr/hts.cr).
157
- - The most challenging part is the return value. In the Crystal language, methods are expected to return only one type. On the other hand, in the Ruby language, methods that return multiple classes are very common. For example, in the Crystal language, the compiler gets confused if the return value is one of six types: Int32, Int64, Float32, Float64, Nil, or String. In fact Crystal allows you to do that. But the code gets a little messy. In Ruby, this is very common and doesn't cause any problems.
158
- - Ruby and Crystal are languages that use garbage collection. However, the memory release policy for allocated C structures is slightly different: in Ruby-FFI, you can define a `self.release` method in `FFI::Struct`. This method is called when GC. So you don't have to worry about memory in high-level APIs like Bam::Record or Bcf::Record, etc. Crystal requires you to define a finalize method on each class. So you need to define it in Bam::Record or Bcf::Record.
182
+ #### Use the latest Ruby
183
+
184
+ Use Ruby 3 or newer to take advantage of new features. This is possible because we have a small number of users.
185
+
186
+ #### Keep compatibility with Crystal language
187
+
188
+ Compatibility with Crystal language is important for Ruby-htslib development.
189
+
190
+ - [HTS.cr](https://github.com/bio-cr/hts.cr) - HTSlib bindings for Crystal
191
+
192
+ Return value
193
+
194
+ The most challenging part is the return value. In the Crystal language, methods are expected to return only one type. On the other hand, in the Ruby language, methods that return multiple classes are very common. For example, in the Crystal language, the compiler gets confused if the return value is one of six types: Int32, Int64, Float32, Float64, Nil, or String. In fact Crystal allows you to do that. But the code gets a little messy. In Ruby, this is very common and doesn't cause any problems.
195
+
196
+ Memory management
197
+
198
+ Ruby and Crystal are languages that use garbage collection. However, the memory release policy for allocated C structures is slightly different: in Ruby-FFI, you can define a `self.release` method in `FFI::Struct`. This method is called when GC. So you don't have to worry about memory in high-level APIs like Bam::Record or Bcf::Record, etc. Crystal requires you to define a finalize method on each class. So you need to define it in Bam::Record or Bcf::Record.
199
+
200
+ Macro functions
201
+
202
+ In ruby-htslib, C macro functions are added to `LibHTS`, but in Crystal, `LibHTS` is a Lib, so methods cannot be added. methods are added to `LibHTS2`.
203
+
204
+ #### Naming convention
205
+
206
+ If you are not sure about the naming of a method, follow the Rust-htslib API. This is a very weak rule. if a more appropriate name is found later in Ruby, it will replace it.
159
207
 
160
- Method naming generally follows the Rust-htslib API.
208
+ #### Support for bitfields of structures
161
209
 
162
- #### FFI Extensions
210
+ Since Ruby-FFI does not support structure bit fields, the following extensions are used.
163
211
 
164
- - [ffi-bitfield](https://github.com/kojix2/ffi-bitfield) : Extension of Ruby-FFI to support bitfields.
212
+ - [ffi-bitfield](https://github.com/kojix2/ffi-bitfield) - Extension of Ruby-FFI to support bitfields.
165
213
 
166
214
  #### Automatic validation
167
215
 
@@ -180,7 +228,7 @@ Ruby-htslib is a library under development, so even minor improvements like typo
180
228
  - [financial contributions](https://github.com/sponsors/kojix2)
181
229
 
182
230
  ```markdown
183
- # Ownership and Commitment Rights
231
+ # Ownership and Commit Rights
184
232
 
185
233
  Do you need commit rights to the ruby-htslib repository?
186
234
  Do you want to get admin rights and take over the project?
data/TUTORIAL.md CHANGED
@@ -38,7 +38,7 @@ class Bcf{
38
38
  +each() Enumerable
39
39
  +query()
40
40
  }
41
- class Tbx~Hts~{
41
+ class Tabix~Hts~{
42
42
  +@hts_file : FFI::Struct
43
43
  }
44
44
  class `Bam::Header`{
@@ -46,32 +46,46 @@ class `Bam::Header`{
46
46
  +struct()
47
47
  +target_count()
48
48
  +target_names()
49
+ +name2tid()
50
+ +tid2name()
49
51
  +to_s()
50
52
  }
51
53
  class `Bam::Record` {
52
54
  +@bam1 : FFI::Struct
53
55
  +@header : Bam::Header
54
56
  +struct()
57
+ +qname()
58
+ +qname=()
55
59
  +tid()
56
60
  +tid=()
57
61
  +mtid()
58
62
  +mtid=()
59
63
  +pos()
60
64
  +pos=()
61
- +mpos()
62
- +mpos=()
65
+ +mpos() +mate_pos()
66
+ +mpos=() +mate_pos=()
63
67
  +bin()
64
68
  +bin=()
65
- +qname()
66
- +flag()
67
- +chorm()
69
+ +endpos()
70
+ +chorm() +contig()
71
+ +mate_chrom() +mate_contig()
72
+ +strand()
73
+ +mate_strand()
74
+ +isize() +insert_size()
75
+ +isize=() +insert_size=()
68
76
  +mapq()
77
+ +mapq=()
69
78
  +cigar()
70
- +mate_chrom()
71
- +isize()
72
- +seq()
79
+ +qlen()
80
+ +rlen()
81
+ +seq() +sequence()
82
+ +len()
83
+ +base(n)
73
84
  +qual()
74
85
  +qual_string()
86
+ +base_qual(n)
87
+ +flag()
88
+ +flag=()
75
89
  +aux()
76
90
  +to_s()
77
91
  }
@@ -91,12 +105,19 @@ class `Bcf::Record`{
91
105
  +@bcf1 : FFI::Struct
92
106
  +@header : Bcf::Header
93
107
  +struct()
108
+ +rid()
109
+ +rid=()
94
110
  +chrom()
95
111
  +pos()
112
+ +pos=()
96
113
  +id()
97
- +qual()
114
+ +id=()
115
+ +clear_id()
98
116
  +ref()
99
117
  +alt()
118
+ +alleles()
119
+ +qual()
120
+ +qual=()
100
121
  +filter()
101
122
  +info()
102
123
  +format()
@@ -164,6 +185,20 @@ class Faidx{
164
185
 
165
186
  ```
166
187
 
188
+ ## Installation
189
+
190
+ ```
191
+ gem install htslib
192
+ ```
193
+
194
+ You can check which shared libraries are used by ruby-htslib as follows
195
+
196
+ ```ruby
197
+ require "htslib"
198
+ puts HTS.lib_path
199
+ # => "/home/kojix2/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/htslib-0.2.6/vendor/libhts.so"
200
+ ```
201
+
167
202
  ## HTS::Bam - SAM / BAM / CRAM - Sequence Alignment Map file
168
203
 
169
204
  Reading fields
@@ -194,9 +229,8 @@ bam.close
194
229
  Open with block
195
230
 
196
231
  ```ruby
197
- HTS::Bam.open("test/fixtures/moo.bam") do |b|
198
- b.
199
- do |r|
232
+ HTS::Bam.open("test/fixtures/moo.bam") do |bam|
233
+ bam.each do |record|
200
234
  # ...
201
235
  end
202
236
  end
@@ -220,6 +254,18 @@ in.close
220
254
  out.close
221
255
  ```
222
256
 
257
+ Create index
258
+
259
+ ```ruby
260
+ HTS::Bam.open("foo.bam", build_index: true)
261
+ ```
262
+
263
+ ```
264
+ b = HTS::Bam.open("foo.bam")
265
+ .build_index
266
+ .load_index
267
+ ```
268
+
223
269
  ## HTS::Bcf - VCF / BCF - Variant Call Format file
224
270
 
225
271
  Reading fields
@@ -245,8 +291,8 @@ bcf.close
245
291
  Open with block
246
292
 
247
293
  ```ruby
248
- HTS::Bcf.open("b.bcf") do |b|
249
- b.each do |r|
294
+ HTS::Bcf.open("b.bcf") do |bcf|
295
+ bcf.each do |record|
250
296
  # ...
251
297
  end
252
298
  end
data/lib/hts/bam/auxi.rb CHANGED
@@ -3,8 +3,11 @@
3
3
  # Q. Why is the file name auxi.rb and not aux.rb?
4
4
  #
5
5
  # A. This is for compatibility with Windows.
6
+ #
6
7
  # In Windows, aux is a reserved word
7
- # You cannot create a file named aux. Eww!
8
+ # You cannot create a file named aux.
9
+ #
10
+ # What?! That's crazy!
8
11
 
9
12
  module HTS
10
13
  class Bam < Hts
@@ -15,6 +18,7 @@ module HTS
15
18
  # Ruby's Aux class references a part of it. There is no one-to-one
16
19
  # correspondence between C structures and Ruby's Aux class.
17
20
  class Aux
21
+ include Enumerable
18
22
  attr_reader :record
19
23
 
20
24
  def initialize(record)
@@ -26,27 +30,10 @@ module HTS
26
30
  # which provides methods like `get_int`, `get_float`, etc.
27
31
  # I think they are better than `fetch_int`` and `fetch_float`.
28
32
  def get(key, type = nil)
29
- aux = LibHTS.bam_aux_get(@record.struct, key)
30
- return nil if aux.null?
31
-
32
- type = type ? type.to_s : aux.read_string(1)
33
+ aux_ptr = LibHTS.bam_aux_get(@record.struct, key)
34
+ return nil if aux_ptr.null?
33
35
 
34
- # A (character), B (general array),
35
- # f (real number), H (hexadecimal array),
36
- # i (integer), or Z (string).
37
-
38
- case type
39
- when "i", "I", "c", "C", "s", "S"
40
- LibHTS.bam_aux2i(aux)
41
- when "f", "d"
42
- LibHTS.bam_aux2f(aux)
43
- when "Z", "H"
44
- LibHTS.bam_aux2Z(aux)
45
- when "A" # char
46
- LibHTS.bam_aux2A(aux).chr
47
- else
48
- raise NotImplementedError, "type: #{t}"
49
- end
36
+ get_ruby_aux(aux_ptr, type)
50
37
  end
51
38
 
52
39
  # For compatibility with HTS.cr.
@@ -67,6 +54,80 @@ module HTS
67
54
  def [](key)
68
55
  get(key)
69
56
  end
57
+
58
+ def first
59
+ aux_ptr = first_pointer
60
+ return nil if aux_ptr.null?
61
+
62
+ get_ruby_aux(aux_ptr)
63
+ end
64
+
65
+ def each
66
+ return enum_for(__method__) unless block_given?
67
+
68
+ aux_ptr = first_pointer
69
+ return nil if aux_ptr.null?
70
+
71
+ loop do
72
+ yield get_ruby_aux(aux_ptr)
73
+ aux_ptr = LibHTS.bam_aux_next(@record.struct, aux_ptr)
74
+ break if aux_ptr.null?
75
+ end
76
+ end
77
+
78
+ def to_h
79
+ h = {}
80
+ aux_ptr = first_pointer
81
+ return h if aux_ptr.null?
82
+
83
+ loop do
84
+ key = FFI::Pointer.new(aux_ptr.address - 2).read_string(2)
85
+ h[key] = get_ruby_aux(aux_ptr)
86
+ aux_ptr = LibHTS.bam_aux_next(@record.struct, aux_ptr)
87
+ break if aux_ptr.null?
88
+ end
89
+ h
90
+ end
91
+
92
+ private
93
+
94
+ def first_pointer
95
+ LibHTS.bam_aux_first(@record.struct)
96
+ end
97
+
98
+ def get_ruby_aux(aux_ptr, type = nil)
99
+ type = type ? type.to_s : aux_ptr.read_string(1)
100
+
101
+ # A (character), B (general array),
102
+ # f (real number), H (hexadecimal array),
103
+ # i (integer), or Z (string).
104
+
105
+ case type
106
+ when "i", "I", "c", "C", "s", "S"
107
+ LibHTS.bam_aux2i(aux_ptr)
108
+ when "f", "d"
109
+ LibHTS.bam_aux2f(aux_ptr)
110
+ when "Z", "H"
111
+ LibHTS.bam_aux2Z(aux_ptr)
112
+ when "A" # char
113
+ LibHTS.bam_aux2A(aux_ptr).chr
114
+ when "B" # array
115
+ t2 = aux_ptr.read_string(2)[1] # just a little less efficient
116
+ l = LibHTS.bam_auxB_len(aux_ptr)
117
+ case t2
118
+ when "c", "C", "s", "S", "i", "I"
119
+ # FIXME : Not efficient.
120
+ Array.new(l) { |i| LibHTS.bam_auxB2i(aux_ptr, i) }
121
+ when "f", "d"
122
+ # FIXME : Not efficient.
123
+ Array.new(l) { |i| LibHTS.bam_auxB2f(aux_ptr, i) }
124
+ else
125
+ raise NotImplementedError, "type: #{type} #{t2}"
126
+ end
127
+ else
128
+ raise NotImplementedError, "type: #{type}"
129
+ end
130
+ end
70
131
  end
71
132
  end
72
133
  end
data/lib/hts/bam/flag.rb CHANGED
@@ -69,27 +69,29 @@ module HTS
69
69
  end
70
70
 
71
71
  def &(other)
72
- Flag.new(@value & other.to_i)
72
+ self.class.new(@value & other.to_i)
73
73
  end
74
74
 
75
75
  def |(other)
76
- Flag.new(@value | other.to_i)
76
+ self.class.new(@value | other.to_i)
77
77
  end
78
78
 
79
79
  def ^(other)
80
- Flag.new(@value ^ other.to_i)
80
+ self.class.new(@value ^ other.to_i)
81
81
  end
82
82
 
83
83
  def ~
84
- Flag.new(~@value)
84
+ # FIXME: Only 12bits are used for flags
85
+ # The result is different from the Crystal version.
86
+ self.class.new(~@value)
85
87
  end
86
88
 
87
89
  def <<(f)
88
- Flag.new(@value << f.to_i)
90
+ self.class.new(@value << f.to_i)
89
91
  end
90
92
 
91
93
  def >>(other)
92
- Flag.new(@value >> other.to_i)
94
+ self.class.new(@value >> other.to_i)
93
95
  end
94
96
 
95
97
  def to_i
@@ -6,8 +6,8 @@ module HTS
6
6
  class Bam < Hts
7
7
  # A class for working with alignment header.
8
8
  class Header
9
- def self.parse(str)
10
- new(LibHTS.sam_hdr_parse(str.size, str))
9
+ def self.parse(text)
10
+ new(LibHTS.sam_hdr_parse(text.size, text))
11
11
  end
12
12
 
13
13
  def initialize(arg = nil)
@@ -21,6 +21,8 @@ module HTS
21
21
  else
22
22
  raise TypeError, "Invalid argument"
23
23
  end
24
+
25
+ yield self if block_given?
24
26
  end
25
27
 
26
28
  def struct
@@ -31,11 +33,23 @@ module HTS
31
33
  @sam_hdr.to_ptr
32
34
  end
33
35
 
36
+ def targets
37
+ Array.new(target_count) do |i|
38
+ name = LibHTS.sam_hdr_tid2name(@sam_hdr, i)
39
+ len = LibHTS.sam_hdr_tid2len(@sam_hdr, i)
40
+ { name:, len: }
41
+ end
42
+ end
43
+
34
44
  def target_count
35
45
  # FIXME: sam_hdr_nref
36
46
  @sam_hdr[:n_targets]
37
47
  end
38
48
 
49
+ def target_name(tid)
50
+ tid2name(tid)
51
+ end
52
+
39
53
  def target_names
40
54
  Array.new(target_count) do |i|
41
55
  LibHTS.sam_hdr_tid2name(@sam_hdr, i)
@@ -48,15 +62,20 @@ module HTS
48
62
  end
49
63
  end
50
64
 
51
- # experimental
52
- def add_lines(str)
53
- LibHTS.sam_hdr_add_lines(@sam_hdr, str, 0)
65
+ def write(...)
66
+ add_lines(...)
54
67
  end
55
68
 
56
69
  # experimental
57
- def add_line(type, *args)
58
- args = args.flat_map { |arg| [:string, arg] }
59
- LibHTS.sam_hdr_add_line(@sam_hdr, type, *args, :pointer, FFI::Pointer::NULL)
70
+ def <<(obj)
71
+ case obj
72
+ when Array, Hash
73
+ args = obj.flatten(-1).map { |i| i.to_a if i.is_a?(Hash) }
74
+ add_line(*args)
75
+ else
76
+ add_lines(obj.to_s)
77
+ end
78
+ self
60
79
  end
61
80
 
62
81
  # experimental
@@ -87,8 +106,31 @@ module HTS
87
106
  LibHTS.sam_hdr_str(@sam_hdr)
88
107
  end
89
108
 
109
+ # experimental
110
+ def get_tid(name)
111
+ name2tid(name)
112
+ end
113
+
90
114
  private
91
115
 
116
+ def name2tid(name)
117
+ LibHTS.sam_hdr_name2tid(@sam_hdr, name)
118
+ end
119
+
120
+ def tid2name(tid)
121
+ LibHTS.sam_hdr_tid2name(@sam_hdr, tid)
122
+ end
123
+
124
+ def add_lines(str)
125
+ LibHTS.sam_hdr_add_lines(@sam_hdr, str, 0)
126
+ end
127
+
128
+ def add_line(*args)
129
+ type = args.shift
130
+ args = args.flat_map { |arg| [:string, arg] }
131
+ LibHTS.sam_hdr_add_line(@sam_hdr, type, *args, :pointer, FFI::Pointer::NULL)
132
+ end
133
+
92
134
  def initialize_copy(orig)
93
135
  @sam_hdr = LibHTS.sam_hdr_dup(orig.struct)
94
136
  end