Commit 0882b9ed authored by Mathieu Parent's avatar Mathieu Parent

Add Debian RFC822 parser

parent 188c8221
# frozen_string_literal: true
module Packages
module Debian
# Parse String as Debian RFC822 control data format
# https://manpages.debian.org/unstable/dpkg-dev/deb822.5
class ParseDebian822Service
InvalidDebian822Error = Class.new(StandardError)
def initialize(input)
@input = input
end
def execute
output = {}
@input.each_line('', chomp: true) do |block|
section = {}
section_name, field = nil
block.each_line(chomp: true) do |line|
next if comment_line?(line)
if continuation_line?(line)
raise InvalidDebian822Error, "Parse error. Unexpected continuation line" if field.nil?
section[field] += "\n"
section[field] += line[1..] unless paragraph_separator?(line)
elsif match = match_section_line(line)
section_name = match[:name] if section_name.nil?
field = match[:field].to_sym
raise InvalidDebian822Error, "Duplicate field '#{field}' in section '#{section_name}'" if section.include?(field)
section[field] = match[:value]
else
raise InvalidDebian822Error, "Parse error on line #{line}"
end
end
raise InvalidDebian822Error, "Duplicate section '#{section_name}'" if output[section_name]
output[section_name] = section
end
output
end
private
def comment_line?(line)
line.match?(/^#/)
end
def continuation_line?(line)
line.match?(/^ /)
end
def paragraph_separator?(line)
line == ' .'
end
def match_section_line(line)
line.match(/(?<name>(?<field>^\S+):\s*(?<value>.*))/)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Debian::ParseDebian822Service do
subject { described_class.new(input) }
context 'with dpkg-deb --field output' do
let(:input) do
<<~HEREDOC
Package: libsample0
Source: sample
Version: 1.2.3~alpha2
Architecture: amd64
Maintainer: John Doe <john.doe@example.com>
Installed-Size: 9
Section: libs
Priority: optional
Multi-Arch: same
Homepage: https://gitlab.com/
Description: Some mostly empty lib
Used in GitLab tests.
.
Testing another paragraph.
HEREDOC
end
it 'return as expected, preserving order' do
expected = {
'Package: libsample0' => {
'Package': 'libsample0',
'Source': 'sample',
'Version': '1.2.3~alpha2',
'Architecture': 'amd64',
'Maintainer': 'John Doe <john.doe@example.com>',
'Installed-Size': '9',
'Section': 'libs',
'Priority': 'optional',
'Multi-Arch': 'same',
'Homepage': 'https://gitlab.com/',
'Description': "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph."
}
}
expect(subject.execute.to_s).to eq(expected.to_s)
end
end
context 'with control file' do
let(:input) { fixture_file('packages/debian/sample/debian/control') }
it 'return as expected, preserving order' do
expected = {
'Source: sample' => {
'Source': 'sample',
'Priority': 'optional',
'Maintainer': 'John Doe <john.doe@example.com>',
'Build-Depends': 'debhelper-compat (= 13)',
'Standards-Version': '4.5.0',
'Section': 'libs',
'Homepage': 'https://gitlab.com/',
# 'Vcs-Browser': 'https://salsa.debian.org/debian/sample-1.2.3',
# '#Vcs-Git': 'https://salsa.debian.org/debian/sample-1.2.3.git',
'Rules-Requires-Root': 'no'
},
'Package: sample-dev' => {
'Package': 'sample-dev',
'Section': 'libdevel',
'Architecture': 'any',
'Multi-Arch': 'same',
'Depends': 'libsample0 (= ${binary:Version}), ${misc:Depends}',
'Description': "Some mostly empty developpement files\nUsed in GitLab tests.\n\nTesting another paragraph."
},
'Package: libsample0' => {
'Package': 'libsample0',
'Architecture': 'any',
'Multi-Arch': 'same',
'Depends': '${shlibs:Depends}, ${misc:Depends}',
'Description': "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph."
},
'Package: sample-udeb' => {
'Package': 'sample-udeb',
'Package-Type': 'udeb',
'Architecture': 'any',
'Depends': 'installed-base',
'Description': 'Some mostly empty udeb'
}
}
expect(subject.execute.to_s).to eq(expected.to_s)
end
end
context 'with empty input' do
let(:input) { '' }
it 'return a empty hash' do
expect(subject.execute).to eq({})
end
end
context 'with unexpected continuation line' do
let(:input) { ' continuation' }
it 'raise error' do
expect {subject.execute}.to raise_error(described_class::InvalidDebian822Error, 'Parse error. Unexpected continuation line')
end
end
context 'with duplicate field' do
let(:input) do
<<~HEREDOC
Package: libsample0
Source: sample
Source: sample
HEREDOC
end
it 'raise error' do
expect {subject.execute}.to raise_error(described_class::InvalidDebian822Error, "Duplicate field 'Source' in section 'Package: libsample0'")
end
end
context 'with incorrect input' do
let(:input) do
<<~HEREDOC
Hello
HEREDOC
end
it 'raise error' do
expect {subject.execute}.to raise_error(described_class::InvalidDebian822Error, 'Parse error on line Hello')
end
end
context 'with duplicate section' do
let(:input) do
<<~HEREDOC
Package: libsample0
Package: libsample0
HEREDOC
end
it 'raise error' do
expect {subject.execute}.to raise_error(described_class::InvalidDebian822Error, "Duplicate section 'Package: libsample0'")
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment