xlsxrb 0.1.7 → 0.1.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -2
  3. data/README.md +125 -36
  4. data/Rakefile +36 -7
  5. data/benchmark.rb +32 -4
  6. data/docs/ARCHITECTURE.md +32 -30
  7. data/docs/DEVELOPMENT.md +11 -3
  8. data/docs/QUALITY_ASSURANCE.md +3 -4
  9. data/docs/SPEC_SOURCES.md +7 -1
  10. data/docs/visual/VisualGallery.md +157 -157
  11. data/docs/wasm/ruby.wasm +0 -0
  12. data/lib/ruby_lsp/xlsxrb/addon.rb +1 -1
  13. data/lib/ruby_lsp/xlsxrb/completion_listener.rb +11 -13
  14. data/lib/xlsxrb/elements/coordinate_access.rb +101 -0
  15. data/lib/xlsxrb/elements/row.rb +2 -2
  16. data/lib/xlsxrb/elements/workbook.rb +20 -2
  17. data/lib/xlsxrb/elements/worksheet.rb +94 -124
  18. data/lib/xlsxrb/elements.rb +1 -0
  19. data/lib/xlsxrb/ooxml/cfb.rb +490 -0
  20. data/lib/xlsxrb/ooxml/crypto/agile.rb +285 -0
  21. data/lib/xlsxrb/ooxml/crypto/standard.rb +152 -0
  22. data/lib/xlsxrb/ooxml/crypto.rb +60 -0
  23. data/lib/xlsxrb/ooxml/worksheet_parser.rb +21 -15
  24. data/lib/xlsxrb/stream_row.rb +181 -0
  25. data/lib/xlsxrb/version.rb +1 -1
  26. data/lib/xlsxrb.rb +271 -122
  27. data/sig/generated/xlsxrb/elements/coordinate_access.rbs +70 -0
  28. data/sig/generated/xlsxrb/elements/worksheet.rbs +119 -14
  29. data/sig/generated/xlsxrb/ooxml/cfb.rbs +121 -0
  30. data/sig/generated/xlsxrb/ooxml/crypto/agile.rbs +46 -0
  31. data/sig/generated/xlsxrb/ooxml/crypto/standard.rbs +28 -0
  32. data/sig/generated/xlsxrb/ooxml/crypto.rbs +17 -0
  33. data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +2 -0
  34. data/sig/generated/xlsxrb/stream_row.rbs +127 -0
  35. data/sig/generated/xlsxrb.rbs +135 -71
  36. data/vendor/sdk_runner/Program.cs +3 -1
  37. metadata +13 -1
@@ -2,33 +2,138 @@
2
2
 
3
3
  module Xlsxrb
4
4
  module Elements
5
- # Represents a single worksheet in a workbook.
5
+ # Represents a single fully parsed, in-memory worksheet in a workbook.
6
+ # Provides coordinate random access (sheet["A1"]), row lookups (row_at),
7
+ # and immutable cell updates (update_cell).
6
8
  #
7
9
  # @example Access cells and rows
8
- # sheet = workbook.sheet(0)
10
+ # sheet = workbook.sheet(0).load
9
11
  # cell = sheet["A1"]
10
- # sheet.each_row { |row| puts row.to_a.inspect }
12
+ # row = sheet.row_at(0)
11
13
  #
12
14
  # @api public
13
- class Worksheet < Data
14
- attr_reader name(): untyped
15
+ class Worksheet
16
+ attr_reader name: untyped
15
17
 
16
- attr_reader rows(): untyped
18
+ attr_reader rows: untyped
17
19
 
18
- attr_reader columns(): untyped
20
+ attr_reader columns: untyped
19
21
 
20
- attr_reader charts(): untyped
22
+ attr_reader charts: untyped
21
23
 
22
- attr_reader unmapped_data(): untyped
24
+ attr_reader unmapped_data: untyped
23
25
 
24
- attr_reader errors(): untyped
26
+ attr_reader errors: untyped
25
27
 
26
- def self.new: (untyped name, untyped rows, untyped columns, untyped charts, untyped unmapped_data, untyped errors) -> instance
27
- | (name: untyped, rows: untyped, columns: untyped, charts: untyped, unmapped_data: untyped, errors: untyped) -> instance
28
+ # @param name [String] The worksheet name (max 31 characters).
29
+ # @param rows [Array<Elements::Row>] Rows in the sheet.
30
+ # @param columns [Array<Elements::Column>] Column definitions.
31
+ # @param charts [Array<Hash>] Charts in the sheet.
32
+ # @param unmapped_data [Hash] Additional metadata for round-tripping.
33
+ # @param errors [Array<String>, nil] Validation errors.
34
+ # : (name: String?, ?rows: Array[Elements::Row], ?columns: Array[Elements::Column], ?charts: Array[Hash[Symbol, untyped]], ?unmapped_data: Hash[untyped, untyped], ?errors: Array[String]?) -> void
35
+ def initialize: (name: String?, ?rows: Array[Elements::Row], ?columns: Array[Elements::Column], ?charts: Array[Hash[Symbol, untyped]], ?unmapped_data: Hash[untyped, untyped], ?errors: Array[String]?) -> void
28
36
 
29
- def self.members: () -> [ :name, :rows, :columns, :charts, :unmapped_data, :errors ]
37
+ # Iterate over rows in the worksheet.
38
+ #
39
+ # @example
40
+ # sheet.each do |row|
41
+ # puts row.to_a.inspect
42
+ # end
43
+ #
44
+ # @yield [row]
45
+ # @yieldparam row [Elements::Row]
46
+ # @return [Enumerator, void]
47
+ # @api public
48
+ # : () { (Elements::Row) -> void } -> void
49
+ # : () -> Enumerator[Elements::Row, void]
50
+ def each: () { (Elements::Row) -> void } -> void
51
+ | () -> Enumerator[Elements::Row, void]
30
52
 
31
- def members: () -> [ :name, :rows, :columns, :charts, :unmapped_data, :errors ]
53
+ # Iterate over rows in the worksheet.
54
+ #
55
+ # @example
56
+ # sheet.each_row do |row|
57
+ # puts "Row #{row.index}: #{row.to_a.inspect}"
58
+ # end
59
+ #
60
+ # @yield [row]
61
+ # @yieldparam row [Elements::Row]
62
+ # @return [Enumerator, void]
63
+ # @api public
64
+ # : () { (Elements::Row) -> void } -> void
65
+ # : () -> Enumerator[Elements::Row, void]
66
+ def each_row: () { (Elements::Row) -> void } -> void
67
+ | () -> Enumerator[Elements::Row, void]
68
+
69
+ # Iterate over all cells across rows.
70
+ #
71
+ # @yield [cell]
72
+ # @yieldparam cell [Elements::Cell]
73
+ # @return [Enumerator, void]
74
+ # @api public
75
+ # : () { (Elements::Cell) -> void } -> void
76
+ # : () -> Enumerator[Elements::Cell, void]
77
+ def each_cell: () { (Elements::Cell) -> void } -> void
78
+ | () -> Enumerator[Elements::Cell, void]
79
+
80
+ # Returns whether the worksheet is valid according to OOXML specifications.
81
+ #
82
+ # @return [Boolean]
83
+ # : () -> bool
84
+ def valid?: () -> bool
85
+
86
+ # Returns a new Worksheet with the specified cell updated.
87
+ #
88
+ # @example
89
+ # new_sheet = sheet.update_cell("B1", value: "Updated")
90
+ #
91
+ # @param ref [String] The cell reference (e.g. "B1").
92
+ # @param value [Object] The new cell value.
93
+ # @param style_index [Integer, String, nil] Optional new style index.
94
+ # @param formula [Elements::Formula, nil] Optional new formula.
95
+ # @return [Worksheet] A new Worksheet instance.
96
+ # @api public
97
+ # : (String ref, ?value: untyped, ?style_index: Integer | String | nil, ?formula: Elements::Formula?) -> Elements::Worksheet
98
+ def update_cell: (String ref, ?value: untyped, ?style_index: Integer | String | nil, ?formula: Elements::Formula?) -> Elements::Worksheet
99
+
100
+ # Returns a new Worksheet with attributes replaced (Data-like behavior).
101
+ #
102
+ # @param changes [Hash]
103
+ # @return [Worksheet]
104
+ # @api public
105
+ # : (**untyped) -> Elements::Worksheet
106
+ def with: (**untyped) -> Elements::Worksheet
107
+
108
+ # Support pattern matching.
109
+ # : (Array[Symbol]?) -> Hash[Symbol, untyped]
110
+ def deconstruct_keys: (Array[Symbol]?) -> Hash[Symbol, untyped]
111
+
112
+ # Compare worksheets for equality.
113
+ # : (untyped other) -> bool
114
+ def ==: (untyped other) -> bool
115
+
116
+ alias eql? ==
117
+
118
+ # : () -> Integer
119
+ def hash: () -> Integer
120
+
121
+ # Returns self when load is called on an already in-memory Worksheet.
122
+ #
123
+ # @return [Elements::Worksheet]
124
+ # @api public
125
+ # : () -> Elements::Worksheet
126
+ def load: () -> Elements::Worksheet
127
+
128
+ alias to_worksheet load
129
+
130
+ # Validates worksheet name and rows against OOXML limits.
131
+ #
132
+ # @param name [String]
133
+ # @param rows [Array<Elements::Row>]
134
+ # @return [Array<String>] List of errors.
135
+ # : (untyped name, untyped rows) -> Array[String]
136
+ def self.validate: (untyped name, untyped rows) -> Array[String]
32
137
  end
33
138
  end
34
139
  end
@@ -0,0 +1,121 @@
1
+ # Generated from lib/xlsxrb/ooxml/cfb.rb with RBS::Inline
2
+
3
+ module Xlsxrb
4
+ module Ooxml
5
+ # Pure-Ruby Compound File Binary (CFB / OLE Structured Storage) implementation for [MS-CFB] / [MS-OFFCRYPTO].
6
+ module Cfb
7
+ MAGIC: untyped
8
+
9
+ FREESECT: ::Integer
10
+
11
+ ENDOFCHAIN: ::Integer
12
+
13
+ FATSECT: ::Integer
14
+
15
+ DIFATSECT: ::Integer
16
+
17
+ NOSTREAM: ::Integer
18
+
19
+ OBJ_UNKNOWN: ::Integer
20
+
21
+ OBJ_STORAGE: ::Integer
22
+
23
+ OBJ_STREAM: ::Integer
24
+
25
+ OBJ_ROOT: ::Integer
26
+
27
+ MINI_STREAM_CUTOFF: ::Integer
28
+
29
+ SECTOR_SIZE: ::Integer
30
+
31
+ MINI_SECTOR_SIZE: ::Integer
32
+
33
+ # Represents a directory entry in a Compound File.
34
+ class DirEntry
35
+ attr_accessor name: untyped
36
+
37
+ attr_accessor type: untyped
38
+
39
+ attr_accessor color: untyped
40
+
41
+ attr_accessor left_sibling_id: untyped
42
+
43
+ attr_accessor right_sibling_id: untyped
44
+
45
+ attr_accessor child_id: untyped
46
+
47
+ attr_accessor clsid: untyped
48
+
49
+ attr_accessor state_flags: untyped
50
+
51
+ attr_accessor created_time: untyped
52
+
53
+ attr_accessor modified_time: untyped
54
+
55
+ attr_accessor start_sector: untyped
56
+
57
+ attr_accessor size: untyped
58
+
59
+ attr_accessor entry_id: untyped
60
+
61
+ def initialize: (?name: untyped, ?type: untyped, ?start_sector: untyped, ?size: untyped) -> untyped
62
+
63
+ def stream?: () -> untyped
64
+
65
+ def root?: () -> untyped
66
+
67
+ def storage?: () -> untyped
68
+ end
69
+
70
+ # Reads streams from a Compound File Binary buffer.
71
+ class Reader
72
+ attr_reader entries: untyped
73
+
74
+ def self.cfb?: (untyped data) -> untyped
75
+
76
+ def initialize: (untyped data) -> untyped
77
+
78
+ def stream_names: () -> untyped
79
+
80
+ def read_stream: (untyped name) -> untyped
81
+
82
+ private
83
+
84
+ def parse_header: () -> untyped
85
+
86
+ def sector_offset: (untyped sector_id) -> untyped
87
+
88
+ def read_sector: (untyped sector_id) -> untyped
89
+
90
+ def build_fat: () -> untyped
91
+
92
+ def load_difat_and_fat: () -> untyped
93
+
94
+ def parse_directory: () -> untyped
95
+
96
+ def load_mini_stream: () -> untyped
97
+
98
+ def read_regular_stream_data: (untyped start_sector, untyped total_size) -> untyped
99
+
100
+ def read_mini_stream_data: (untyped start_mini_sector, untyped total_size) -> untyped
101
+ end
102
+
103
+ # Writes named streams into a Compound File Binary (v3, 512-byte sectors) format with Mini Stream support.
104
+ class Writer
105
+ def self.write: (untyped streams) -> untyped
106
+
107
+ def initialize: (untyped streams) -> untyped
108
+
109
+ def build: () -> untyped
110
+
111
+ private
112
+
113
+ def serialize_dir_entry: (untyped entry) -> untyped
114
+
115
+ def insert_entry_to_tree: (untyped entries, untyped root_node, untyped new_node) -> untyped
116
+
117
+ def compare_entry_names: (untyped a_name, untyped b_name) -> untyped
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,46 @@
1
+ # Generated from lib/xlsxrb/ooxml/crypto/agile.rb with RBS::Inline
2
+
3
+ module Xlsxrb
4
+ module Ooxml
5
+ module Crypto
6
+ # Implements Microsoft Office Agile Encryption specified in [MS-OFFCRYPTO] Section 2.3.4.
7
+ class Agile
8
+ BLOCK_KEY_VERIFIER_INPUT: untyped
9
+
10
+ BLOCK_KEY_VERIFIER_VALUE: untyped
11
+
12
+ BLOCK_KEY_KEY: untyped
13
+
14
+ BLOCK_KEY_INTEGRITY_KEY: untyped
15
+
16
+ BLOCK_KEY_INTEGRITY_VALUE: untyped
17
+
18
+ SEGMENT_SIZE: ::Integer
19
+
20
+ # Decrypts an encrypted package given the EncryptionInfo stream data and EncryptedPackage stream data.
21
+ def self.decrypt: (untyped encryption_info_bytes, untyped encrypted_package_bytes, untyped password) -> untyped
22
+
23
+ # Encrypts a plain zip payload with the given password into EncryptionInfo and EncryptedPackage streams.
24
+ def self.encrypt: (untyped plain_bytes, untyped password) -> untyped
25
+
26
+ private def self.parse_encryption_info: (untyped bytes) -> untyped
27
+
28
+ private def self.digest_for: (untyped name) -> untyped
29
+
30
+ private def self.cipher_name_for: (untyped algo, untyped key_bits) -> untyped
31
+
32
+ private def self.derive_h_final: (untyped digest, untyped password, untyped salt, untyped spin_count) -> untyped
33
+
34
+ private def self.derive_key: (untyped digest, untyped h_final, untyped block_key, untyped key_bytes) -> untyped
35
+
36
+ private def self.aes_decrypt: (untyped cipher_name, untyped key, untyped init_vector, untyped data) -> untyped
37
+
38
+ private def self.aes_encrypt: (untyped cipher_name, untyped key, untyped init_vector, untyped data) -> untyped
39
+
40
+ private def self.aes_decrypt_block: (untyped cipher_name, untyped key, untyped init_vector, untyped data) -> untyped
41
+
42
+ private def self.aes_encrypt_block: (untyped cipher_name, untyped key, untyped init_vector, untyped data) -> untyped
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ # Generated from lib/xlsxrb/ooxml/crypto/standard.rb with RBS::Inline
2
+
3
+ module Xlsxrb
4
+ module Ooxml
5
+ module Crypto
6
+ # Implements Microsoft Office Standard Encryption specified in [MS-OFFCRYPTO] Section 2.3.6.
7
+ class Standard
8
+ CSP_NAME: untyped
9
+
10
+ ITERATION_COUNT: ::Integer
11
+
12
+ # Decrypts a Standard-encrypted package.
13
+ def self.decrypt: (untyped encryption_info_bytes, untyped encrypted_package_bytes, untyped password) -> untyped
14
+
15
+ # Encrypts plain zip data into Standard Encryption streams.
16
+ def self.encrypt: (untyped plain_bytes, untyped password) -> untyped
17
+
18
+ private def self.parse_encryption_info: (untyped bytes) -> untyped
19
+
20
+ private def self.derive_key: (untyped password, untyped salt, ?untyped block_num) -> untyped
21
+
22
+ private def self.aes_ecb_encrypt: (untyped key, untyped data) -> untyped
23
+
24
+ private def self.aes_ecb_decrypt: (untyped key, untyped data) -> untyped
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # Generated from lib/xlsxrb/ooxml/crypto.rb with RBS::Inline
2
+
3
+ module Xlsxrb
4
+ module Ooxml
5
+ # High-level encryption and decryption facade for [MS-OFFCRYPTO] Excel document protection.
6
+ module Crypto
7
+ # Checks if the binary data is an encrypted Compound File Binary package.
8
+ def self.encrypted?: (untyped data) -> untyped
9
+
10
+ # Decrypts an encrypted XLSX (CFB) package with the given password.
11
+ def self.decrypt: (untyped cfb_data, untyped password) -> untyped
12
+
13
+ # Encrypts a plain ZIP payload with the given password into an encrypted CFB package.
14
+ def self.encrypt: (untyped plain_zip_data, untyped password, ?mode: untyped) -> untyped
15
+ end
16
+ end
17
+ end
@@ -62,6 +62,8 @@ module Xlsxrb
62
62
  def self.fast_scan_rows_direct: (untyped xml_src, untyped shared_strings, untyped part_name) ?{ (?) -> untyped } -> untyped
63
63
 
64
64
  def self.fast_parse_cells_direct: (untyped xml, untyped from, untyped to, untyped shared_strings, untyped row_source) -> untyped
65
+
66
+ def self.fast_scan_cells_direct: (untyped xml, untyped from, untyped to, untyped shared_strings, untyped row_source) ?{ (?) -> untyped } -> untyped
65
67
  end
66
68
  end
67
69
  end
@@ -0,0 +1,127 @@
1
+ # Generated from lib/xlsxrb/stream_row.rb with RBS::Inline
2
+
3
+ module Xlsxrb
4
+ # Streaming row implementation that parses cells on-demand / lazily.
5
+ # Provides O(1) memory consumption even for rows with tens of thousands of columns.
6
+ #
7
+ # @example Streaming cells one-by-one (O(1) memory)
8
+ # row.each_cell do |cell|
9
+ # puts "#{cell.ref}: #{cell.value}"
10
+ # end
11
+ #
12
+ # @example Random access or array conversion (cached on-demand)
13
+ # cell = row[0]
14
+ # values = row.to_a
15
+ #
16
+ # @api public
17
+ class StreamRow
18
+ attr_reader index: untyped
19
+
20
+ attr_reader height: untyped
21
+
22
+ attr_reader hidden: untyped
23
+
24
+ attr_reader custom_height: untyped
25
+
26
+ attr_reader outline_level: untyped
27
+
28
+ # @param index [Integer] 0-based row index.
29
+ # @param xml_bytes [String] Raw ASCII-8BIT XML bytes.
30
+ # @param from [Integer] Byte offset where cells start.
31
+ # @param to [Integer] Byte offset where cells end.
32
+ # @param shared_strings [Array<String>] Shared strings table.
33
+ # @param height [Float, Integer, nil] Row height in points.
34
+ # @param hidden [Boolean] Whether the row is hidden.
35
+ # @param custom_height [Boolean] Whether custom height is set.
36
+ # @param outline_level [Integer, nil] Grouping/outline level.
37
+ # : (index: Integer, xml_bytes: String, from: Integer, to: Integer, shared_strings: Array[String], ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
38
+ def initialize: (index: Integer, xml_bytes: String, from: Integer, to: Integer, shared_strings: Array[String], ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
39
+
40
+ # Iterate over cells in this streaming row one by one.
41
+ #
42
+ # @yield [cell]
43
+ # @yieldparam cell [Elements::Cell]
44
+ # @return [Enumerator, void]
45
+ # @api public
46
+ # : () { (Elements::Cell) -> void } -> void
47
+ # : () -> Enumerator[Elements::Cell, void]
48
+ def each_cell: () { (Elements::Cell) -> void } -> void
49
+ | () -> Enumerator[Elements::Cell, void]
50
+
51
+ # Iterate over cells in this streaming row.
52
+ #
53
+ # @yield [cell]
54
+ # @yieldparam cell [Elements::Cell]
55
+ # @return [Enumerator, void]
56
+ # @api public
57
+ # : () { (Elements::Cell) -> void } -> void
58
+ # : () -> Enumerator[Elements::Cell, void]
59
+ def each: () { (Elements::Cell) -> void } -> void
60
+ | () -> Enumerator[Elements::Cell, void]
61
+
62
+ # Returns all cells as an Array. Cached on first access.
63
+ #
64
+ # @return [Array<Elements::Cell>]
65
+ # @api public
66
+ # : () -> Array[Elements::Cell]
67
+ def cells: () -> Array[Elements::Cell]
68
+
69
+ # Access a cell by 0-based column index, or access row attributes via Symbol.
70
+ #
71
+ # @param col_index [Integer, Symbol] Column index or attribute symbol.
72
+ # @return [Elements::Cell, Object, nil]
73
+ # @api public
74
+ # : (Integer | Symbol col_index) -> untyped
75
+ def []: (Integer | Symbol col_index) -> untyped
76
+
77
+ # Access a cell by 0-based column index.
78
+ #
79
+ # @param col_index [Integer] 0-based column index.
80
+ # @return [Elements::Cell, nil]
81
+ # @api public
82
+ # : (Integer col_index) -> Elements::Cell?
83
+ def cell_at: (Integer col_index) -> Elements::Cell?
84
+
85
+ # Convert row cells to an Array of raw values (sparse columns get nil).
86
+ #
87
+ # @return [Array<Object>]
88
+ # @api public
89
+ # : () -> Array[untyped]
90
+ def to_a: () -> Array[untyped]
91
+
92
+ # Returns cell values as an Array.
93
+ #
94
+ # @return [Array<Object>]
95
+ # @api public
96
+ # : () -> Array[untyped]
97
+ def values: () -> Array[untyped]
98
+
99
+ # Returns whether the row is valid according to OOXML specifications.
100
+ #
101
+ # @return [Boolean]
102
+ # @api public
103
+ # : () -> bool
104
+ def valid?: () -> bool
105
+
106
+ # Unmapped metadata for compatibility with Elements::Row.
107
+ #
108
+ # @return [Hash]
109
+ # @api public
110
+ # : () -> Hash[untyped, untyped]
111
+ def unmapped_data: () -> Hash[untyped, untyped]
112
+
113
+ # Validation errors for compatibility with Elements::Row.
114
+ #
115
+ # @return [Array<String>]
116
+ # @api public
117
+ # : () -> Array[String]
118
+ def errors: () -> Array[String]
119
+
120
+ # Human-readable representation.
121
+ #
122
+ # @return [String]
123
+ # @api public
124
+ # : () -> String
125
+ def inspect: () -> String
126
+ end
127
+ end