]>
git.proxmox.com Git - proxmox-apt.git/blob - src/repositories/file/sources_parser.rs
e824f3d2b40f3b5c7c5145196300291994d09eee
1 use std
::convert
::TryInto
;
3 use std
::iter
::Iterator
;
5 use anyhow
::{bail, Error}
;
7 use crate::repositories
::{
8 APTRepository
, APTRepositoryFileType
, APTRepositoryOption
, APTRepositoryPackageType
,
11 use super::APTRepositoryParser
;
13 pub struct APTSourcesFileParser
<R
: BufRead
> {
19 /// See `man sources.list` and `man deb822` for the format specification.
20 impl<R
: BufRead
> APTSourcesFileParser
<R
> {
21 pub fn new(reader
: R
) -> Self {
25 comment
: String
::new(),
29 /// Based on APT's `StringToBool` in `strutl.cc`
30 fn string_to_bool(string
: &str, default: bool
) -> bool
{
31 let string
= string
.trim_matches(|c
| char::is_ascii_whitespace(&c
));
32 let string
= string
.to_lowercase();
35 "1" | "yes" | "true" | "with" | "on" | "enable" => true,
36 "0" | "no" | "false" | "without" | "off" | "disable" => false,
41 /// Checks if `key` is valid according to deb822
42 fn valid_key(key
: &str) -> bool
{
43 if key
.starts_with('
-'
) {
46 return key
.chars().all(|c
| matches
!(c
, '
!'
..='
9'
| '
;'
..='
~'
));
49 /// Try parsing a repository in stanza format from `lines`.
51 /// Returns `Ok(None)` when no stanza can be found.
53 /// Comments are added to `self.comments`. If a stanza can be found,
54 /// `self.comment` is added to the repository's `comment` property.
56 /// Fully commented out stanzas are treated as comments.
57 fn parse_stanza(&mut self, lines
: &str) -> Result
<Option
<APTRepository
>, Error
> {
58 let mut repo
= APTRepository
::new(APTRepositoryFileType
::Sources
);
60 // Values may be folded into multiple lines.
61 // Those lines have to start with a space or a tab.
62 let lines
= lines
.replace("\n ", " ");
63 let lines
= lines
.replace("\n\t", " ");
65 let mut got_something
= false;
67 for line
in lines
.lines() {
68 let line
= line
.trim_matches(|c
| char::is_ascii_whitespace(&c
));
73 if let Some(commented_out
) = line
.strip_prefix('
#') {
74 self.comment
= format
!("{}{}\n", self.comment
, commented_out
);
78 if let Some(mid
) = line
.find('
:'
) {
79 let (key
, value_str
) = line
.split_at(mid
);
80 let value_str
= &value_str
[1..];
81 let key
= key
.trim_matches(|c
| char::is_ascii_whitespace(&c
));
84 bail
!("option has no key: '{}'", line
);
87 if value_str
.is_empty() {
89 eprintln
!("option has no value: '{}'", line
);
93 if !Self::valid_key(key
) {
95 eprintln
!("option with invalid key '{}'", key
);
99 let values
: Vec
<String
> = value_str
100 .split_ascii_whitespace()
101 .map(|value
| value
.to_string())
104 match &key
.to_lowercase()[..] {
106 if !repo
.types
.is_empty() {
107 eprintln
!("key 'Types' was defined twice");
109 let mut types
= Vec
::<APTRepositoryPackageType
>::new();
110 for package_type
in values
{
111 types
.push((&package_type
[..]).try_into()?
);
116 if !repo
.uris
.is_empty() {
117 eprintln
!("key 'URIs' was defined twice");
122 if !repo
.suites
.is_empty() {
123 eprintln
!("key 'Suites' was defined twice");
125 repo
.suites
= values
;
128 if !repo
.components
.is_empty() {
129 eprintln
!("key 'Components' was defined twice");
131 repo
.components
= values
;
134 repo
.set_enabled(Self::string_to_bool(value_str
, true));
136 _
=> repo
.options
.push(APTRepositoryOption
{
137 key
: key
.to_string(),
142 bail
!("got invalid line - '{:?}'", line
);
145 got_something
= true;
152 repo
.comment
= std
::mem
::take(&mut self.comment
);
157 /// Helper function for `parse_repositories`.
161 repos
: &mut Vec
<APTRepository
>,
162 ) -> Result
<(), Error
> {
163 match self.parse_stanza(lines
) {
169 Err(err
) => bail
!("malformed entry in stanza {} - {}", self.stanza_nr
, err
),
176 impl<R
: BufRead
> APTRepositoryParser
for APTSourcesFileParser
<R
> {
177 fn parse_repositories(&mut self) -> Result
<Vec
<APTRepository
>, Error
> {
178 let mut repos
= vec
![];
179 let mut lines
= String
::new();
182 let old_length
= lines
.len();
183 match self.input
.read_line(&mut lines
) {
184 Err(err
) => bail
!("input error - {}", err
),
186 self.try_parse_stanza(&lines
[..], &mut repos
)?
;
190 if (&lines
[old_length
..])
191 .trim_matches(|c
| char::is_ascii_whitespace(&c
))
194 // detected end of stanza
195 self.try_parse_stanza(&lines
[..], &mut repos
)?
;