Files
rdb/lib/rdb/table.rb
T

98 lines
2.4 KiB
Ruby

# frozen_string_literal: true
module Rdb
# A table is a head line of column names, a dash line, then one
# tab-separated row per line. A list is the same data turned sideways:
# it starts with a newline and holds "Column<tab>value" lines.
class Table
attr_reader :head, :rows
attr_accessor :list
def initialize(head, rows, list: false)
@head = head
@rows = rows
@list = list
end
def self.dash_line?(cells)
cells.all? { |c| c.match?(/\A\s*-*\s*\z/) } && cells.any? { |c| c.include?('-') }
end
def self.parse(text)
return parse_list(text) if text.start_with?("\n")
lines = text.lines.map { |l| l.chomp("\n") }
head = (lines.shift || '').split("\t", -1)
lines.shift if lines.first && dash_line?(lines.first.split("\t", -1))
rows = lines.map { |l| l.split("\t", -1) }
new(head, rows.map { |r| r.fill('', r.size...head.size) })
end
def self.parse_list(text)
head = []
records = []
rec = nil
text.sub(/\A\n/, '').each_line do |line|
line = line.chomp("\n")
if line.strip.empty?
rec = nil
next
end
name, value = line.split("\t", 2)
value ||= ''
head << name unless head.include?(name)
rec = nil if rec && name == head.first && rec.key?(name)
unless rec
rec = {}
records << rec
end
rec[name] = value
end
new(head, records.map { |r| head.map { |h| r.fetch(h, '') } }, list: true)
end
def self.read(io)
parse(io.read)
end
def self.open(path)
path == '-' ? read($stdin) : parse(File.read(path))
end
def index_of(name)
@head.index(name) || Rdb.die("no column named '#{name}'")
end
def to_s
return list_text if @list
out = +''
out << @head.join("\t") << "\n"
out << Table.dash_row(@head) << "\n"
@rows.each { |r| out << r.join("\t") << "\n" }
out
end
def self.dash_row(head)
head.map { |h| '-' * [h.size, 1].max }.join("\t")
end
def list_text
out = +"\n"
@rows.each do |r|
@head.each_with_index { |h, i| out << h << "\t" << r[i].to_s << "\n" }
out << "\n"
end
out
end
def write(out = $stdout)
out.write to_s
end
def with(head: @head, rows: @rows)
Table.new(head, rows, list: @list)
end
end
end