Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions lib/ruby-mpd/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def quotable_param(value)
:decoders, :listplaylistinfo, :playlistinfo]

# Parses key-value pairs into correct class.
def parse_key(key, value)
def parse_key(command, key, value)
if INT_KEYS.include? key
value.to_i
elsif FLOAT_KEYS.include? key
Expand All @@ -71,9 +71,7 @@ def parse_key(key, value)
value != '0'
elsif SYM_KEYS.include? key
value.to_sym
elsif key == :playlist && !value.to_i.zero?
# doc states it's an unsigned int, meaning if we get 0,
# then it's a name string.
elsif key == :playlist && command != :listplaylists
value.to_i
elsif key == :db_update
Time.at(value.to_i)
Expand All @@ -93,21 +91,21 @@ def parse_key(key, value)
end

# Parses a single response line into a key-object (value) pair.
def parse_line(line)
def parse_line(command, line)
key, value = line.split(/:\s?/, 2)
key = key.downcase.to_sym
return key, parse_key(key, value.chomp)
return key, parse_key(command, key, value.chomp)
end

# This builds a hash out of lines returned from the server,
# elements parsed into the correct type.
#
# The end result is a hash containing the proper key/value pairs
def build_hash(string)
def build_hash(command, string)
return {} if string.nil?

string.lines.each_with_object({}) do |line, hash|
key, object = parse_line(line)
key, object = parse_line(command, line)

# if val appears more than once, make an array of vals.
if hash.include? key
Expand Down Expand Up @@ -137,7 +135,7 @@ def make_chunks(string)
# @return [Array<Hash>, Array<String>, String, Integer] Parsed response.
def parse_response(command, string)
if command == :listall # Explicitly handle :listall (#files) -> always return a Hash
return build_hash(string)
return build_hash(command, string)
elsif command == :listallinfo
# We do not care about directories or playlists,
# and leaving them in breaks the heuristic used by `make_chunks`.
Expand All @@ -160,7 +158,13 @@ def build_response(command, string)
is_hash = chunks.any? { |chunk| chunk.include? "\n" }

list = chunks.inject([]) do |result, chunk|
result << (is_hash ? build_hash(chunk) : parse_line(chunk)[1]) # parse_line(chunk)[1] is object
result << begin
if is_hash
build_hash(command, chunk)
else
parse_line(command, chunk)[1] # object
end
end
end

# if list has only one element and not set to explicit array, return it, else return array
Expand Down
33 changes: 19 additions & 14 deletions spec/ruby-mpd/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,58 +69,63 @@
describe "#parse_key" do
context "with valid INT_KEYS" do
MPD::Parser::INT_KEYS.each do |key|
it { expect(subject.send(:parse_key, key, '32')).to eql(32) }
it { expect(subject.send(:parse_key, :status, key, '32')).to eql(32) }
end
end

context "with valid SYM_KEYS" do
MPD::Parser::SYM_KEYS.each do |key|
it { expect(subject.send(:parse_key, key, 'value')).to eql(:value) }
it { expect(subject.send(:parse_key, :status, key, 'value')).to eql(:value) }
end
end

context "with valid FLOAT_KEYS" do
MPD::Parser::FLOAT_KEYS.each do |key|
it { expect(subject.send(:parse_key, key, 10)).to eql(10.0) }
it { expect(subject.send(:parse_key, key, 'nan')).to be(Float::NAN) }
it { expect(subject.send(:parse_key, :status, key, 10)).to eql(10.0) }
it { expect(subject.send(:parse_key, :status, key, 'nan')).to be(Float::NAN) }
end
end

context "with valid BOOL_KEYS" do
MPD::Parser::BOOL_KEYS.each do |key|
it { expect(subject.send(:parse_key, key, '1')).to be_truthy }
it { expect(subject.send(:parse_key, key, '0')).to be_falsey }
it { expect(subject.send(:parse_key, :status, key, '1')).to be_truthy }
it { expect(subject.send(:parse_key, :status, key, '0')).to be_falsey }
end
end

context "with :playlist key" do
it { expect(subject.send(:parse_key, :playlist, '32')).to eql(32) }
it { expect(subject.send(:parse_key, :playlist, '0')).to eql('0') }
it { expect(subject.send(:parse_key, :status, :playlist, '32')).to eql(32) }
it { expect(subject.send(:parse_key, :status, :playlist, '0')).to eql(0) }

context "for :listplaylists command" do
it { expect(subject.send(:parse_key, :listplaylists, :playlist, '0')).to eql('0') }
it { expect(subject.send(:parse_key, :listplaylists, :playlist, '70s')).to eql('70s') }
end
end

context "with :db_update key" do
expected_time = Time.at(1434024873)
it { expect(subject.send(:parse_key, :db_update, '1434024873').utc)
it { expect(subject.send(:parse_key, :status, :db_update, '1434024873').utc)
.to eql(expected_time.utc) }
end

context "with :'last-modified' key" do
let(:time) { Time.parse("Thu Nov 29 14:33:20 GMT 2001") }
it { expect(subject.send(:parse_key, :"last-modified", time.utc.iso8601))
it { expect(subject.send(:parse_key, :status, :"last-modified", time.utc.iso8601))
.to eql(time) }
end

context "with :time, :audio key" do
it { expect(subject.send(:parse_key, :time, '123')).to eql([nil, 123]) }
it { expect(subject.send(:parse_key, :time, '99:123')).to eql([99, 123]) }
it { expect(subject.send(:parse_key, :audio, '12:33')).to eql([12, 33]) }
it { expect(subject.send(:parse_key, :status, :time, '123')).to eql([nil, 123]) }
it { expect(subject.send(:parse_key, :status, :time, '99:123')).to eql([99, 123]) }
it { expect(subject.send(:parse_key, :status, :audio, '12:33')).to eql([12, 33]) }
end
end

describe "#parse_line" do
context "with a valid response line" do
let(:response) { "UPTIME:32\n xxxx" }
it { expect(subject.send(:parse_line, response)).to eql([:uptime, 32]) }
it { expect(subject.send(:parse_line, :stats, response)).to eql([:uptime, 32]) }
end
end

Expand Down
4 changes: 2 additions & 2 deletions test/test_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ def test_parse_empty_listall_command
end

def test_parse_playlist_uint
assert_equal @parser.parse_key(:playlist, '31'), 31
assert_equal @parser.parse_key(:status, :playlist, '31'), 31
end

def test_parse_playlist_name
assert_equal @parser.parse_key(:playlist, 'leftover/classics.m3u'), 'leftover/classics.m3u'
assert_equal @parser.parse_key(:listplaylists, :playlist, 'leftover/classics.m3u'), 'leftover/classics.m3u'
end

end