]>
git.proxmox.com Git - proxmox-apt.git/blob - src/repositories/file/sources_parser.rs
2 use std
::iter
::Iterator
;
4 use anyhow
::{bail, Error}
;
6 use crate::repositories
::{
7 APTRepository
, APTRepositoryFileType
, APTRepositoryOption
, APTRepositoryPackageType
,
10 use super::APTRepositoryParser
;
12 pub struct APTSourcesFileParser
<R
: BufRead
> {
18 /// See `man sources.list` and `man deb822` for the format specification.
19 impl<R
: BufRead
> APTSourcesFileParser
<R
> {
20 pub fn new(reader
: R
) -> Self {
24 comment
: String
::new(),
28 /// Based on APT's `StringToBool` in `strutl.cc`
29 fn string_to_bool(string
: &str, default: bool
) -> bool
{
30 let string
= string
.trim_matches(|c
| char::is_ascii_whitespace(&c
));
31 let string
= string
.to_lowercase();
34 "1" | "yes" | "true" | "with" | "on" | "enable" => true,
35 "0" | "no" | "false" | "without" | "off" | "disable" => false,
40 /// Checks if `key` is valid according to deb822
41 fn valid_key(key
: &str) -> bool
{
42 if key
.starts_with('
-'
) {
45 return key
.chars().all(|c
| matches
!(c
, '
!'
..='
9'
| '
;'
..='
~'
));
48 /// Try parsing a repository in stanza format from `lines`.
50 /// Returns `Ok(None)` when no stanza can be found.
52 /// Comments are added to `self.comments`. If a stanza can be found,
53 /// `self.comment` is added to the repository's `comment` property.
55 /// Fully commented out stanzas are treated as comments.
56 fn parse_stanza(&mut self, lines
: &str) -> Result
<Option
<APTRepository
>, Error
> {
57 let mut repo
= APTRepository
::new(APTRepositoryFileType
::Sources
);
59 // Values may be folded into multiple lines.
60 // Those lines have to start with a space or a tab.
61 let lines
= lines
.replace("\n ", " ");
62 let lines
= lines
.replace("\n\t", " ");
64 let mut got_something
= false;
66 for line
in lines
.lines() {
67 let line
= line
.trim_matches(|c
| char::is_ascii_whitespace(&c
));
72 if let Some(commented_out
) = line
.strip_prefix('
#') {
73 self.comment
= format
!("{}{}\n", self.comment
, commented_out
);
77 if let Some(mid
) = line
.find('
:'
) {
78 let (key
, value_str
) = line
.split_at(mid
);
79 let value_str
= &value_str
[1..];
80 let key
= key
.trim_matches(|c
| char::is_ascii_whitespace(&c
));
83 bail
!("option has no key: '{}'", line
);
86 if value_str
.is_empty() {
88 eprintln
!("option has no value: '{}'", line
);
92 if !Self::valid_key(key
) {
94 eprintln
!("option with invalid key '{}'", key
);
98 let values
: Vec
<String
> = value_str
99 .split_ascii_whitespace()
100 .map(|value
| value
.to_string())
103 match &key
.to_lowercase()[..] {
105 if !repo
.types
.is_empty() {
106 eprintln
!("key 'Types' was defined twice");
108 let mut types
= Vec
::<APTRepositoryPackageType
>::new();
109 for package_type
in values
{
110 types
.push((&package_type
[..]).try_into()?
);
115 if !repo
.uris
.is_empty() {
116 eprintln
!("key 'URIs' was defined twice");
121 if !repo
.suites
.is_empty() {
122 eprintln
!("key 'Suites' was defined twice");
124 repo
.suites
= values
;
127 if !repo
.components
.is_empty() {
128 eprintln
!("key 'Components' was defined twice");
130 repo
.components
= values
;
133 repo
.set_enabled(Self::string_to_bool(value_str
, true));
135 _
=> repo
.options
.push(APTRepositoryOption
{
136 key
: key
.to_string(),
141 bail
!("got invalid line - '{:?}'", line
);
144 got_something
= true;
151 repo
.comment
= std
::mem
::take(&mut self.comment
);
156 /// Helper function for `parse_repositories`.
160 repos
: &mut Vec
<APTRepository
>,
161 ) -> Result
<(), Error
> {
162 match self.parse_stanza(lines
) {
168 Err(err
) => bail
!("malformed entry in stanza {} - {}", self.stanza_nr
, err
),
175 impl<R
: BufRead
> APTRepositoryParser
for APTSourcesFileParser
<R
> {
176 fn parse_repositories(&mut self) -> Result
<Vec
<APTRepository
>, Error
> {
177 let mut repos
= vec
![];
178 let mut lines
= String
::new();
181 let old_length
= lines
.len();
182 match self.input
.read_line(&mut lines
) {
183 Err(err
) => bail
!("input error - {}", err
),
185 self.try_parse_stanza(&lines
[..], &mut repos
)?
;
189 if (lines
[old_length
..])
190 .trim_matches(|c
| char::is_ascii_whitespace(&c
))
193 // detected end of stanza
194 self.try_parse_stanza(&lines
[..], &mut repos
)?
;