Skip to main content

debian_watch/
lib.rs

1#![deny(missing_docs)]
2//! Formatting-preserving parser and editor for Debian watch files
3//!
4//! # Example
5//!
6//! ```rust,ignore
7//! // For line-based formats (v1-4):
8//! // Note: This example requires the "linebased" feature (enabled by default)
9//! let wf = debian_watch::linebased::WatchFile::new(None);
10//! assert_eq!(wf.version(), debian_watch::DEFAULT_VERSION);
11//! assert_eq!("", wf.to_string());
12//!
13//! let wf = debian_watch::linebased::WatchFile::new(Some(4));
14//! assert_eq!(wf.version(), 4);
15//! assert_eq!("version=4\n", wf.to_string());
16//!
17//! let wf: debian_watch::linebased::WatchFile = r#"version=4
18//! opts=foo=blah https://foo.com/bar .*/v?(\d\S+)\.tar\.gz
19//! "#.parse().unwrap();
20//! assert_eq!(wf.version(), 4);
21//! assert_eq!(wf.entries().collect::<Vec<_>>().len(), 1);
22//! let entry = wf.entries().next().unwrap();
23//! assert_eq!(entry.opts(), maplit::hashmap! {
24//!    "foo".to_string() => "blah".to_string(),
25//! });
26//! assert_eq!(&entry.url(), "https://foo.com/bar");
27//! assert_eq!(entry.matching_pattern().as_deref(), Some(".*/v?(\\d\\S+)\\.tar\\.gz"));
28//! ```
29
30#[cfg(feature = "linebased")]
31mod lex;
32#[cfg(feature = "linebased")]
33/// Line-based watch file format parser (versions 1-4)
34pub mod linebased;
35
36#[cfg(all(feature = "deb822", feature = "linebased"))]
37mod convert;
38#[cfg(feature = "deb822")]
39pub mod deb822;
40#[cfg(all(feature = "discover", any(feature = "linebased", feature = "deb822")))]
41pub mod discover;
42pub mod mangle;
43#[cfg(feature = "pgp")]
44pub mod pgp;
45pub mod release;
46pub mod search;
47
48/// Any watch files without a version are assumed to be
49/// version 1.
50pub const DEFAULT_VERSION: u32 = 1;
51
52#[cfg(any(feature = "linebased", feature = "deb822"))]
53pub mod parse;
54mod subst;
55mod types;
56/// Default user agent string used for HTTP requests
57pub const DEFAULT_USER_AGENT: &str = concat!("debian-watch-rs/", env!("CARGO_PKG_VERSION"));
58
59pub use release::Release;
60pub use types::*;
61
62/// Let's start with defining all kinds of tokens and
63/// composite nodes.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
65#[allow(non_camel_case_types, missing_docs, clippy::upper_case_acronyms)]
66#[repr(u16)]
67pub(crate) enum SyntaxKind {
68    KEY = 0,
69    VALUE,
70    EQUALS,
71    QUOTE,
72    COMMA,
73    CONTINUATION,
74    NEWLINE,
75    WHITESPACE, // whitespaces is explicit
76    COMMENT,    // comments
77    ERROR,      // as well as errors
78
79    // composite nodes
80    ROOT,             // The entire file
81    VERSION,          // "version=x\n"
82    ENTRY,            // "opts=foo=blah https://foo.com/bar .*/v?(\d\S+)\.tar\.gz\n"
83    OPTS_LIST,        // "opts=foo=blah"
84    OPTION,           // "foo=blah"
85    OPTION_SEPARATOR, // "," (comma separator between options)
86    URL,              // "https://foo.com/bar"
87    MATCHING_PATTERN, // ".*/v?(\d\S+)\.tar\.gz"
88    VERSION_POLICY,   // "debian"
89    SCRIPT,           // "uupdate"
90}
91
92/// Convert our `SyntaxKind` into the rowan `SyntaxKind`.
93impl From<SyntaxKind> for rowan::SyntaxKind {
94    fn from(kind: SyntaxKind) -> Self {
95        Self(kind as u16)
96    }
97}
98
99// Only export traits - specific implementations are in their modules
100// Users access linebased types via debian_watch::linebased::WatchFile
101// Users access deb822 types via debian_watch::deb822::WatchFile
102
103#[cfg(all(feature = "deb822", feature = "linebased"))]
104pub use crate::convert::{convert_to_v5, ConversionError};
105
106#[cfg(all(test, feature = "linebased"))]
107mod tests {
108    use crate::linebased::WatchFile;
109
110    #[test]
111    fn test_create_watchfile() {
112        let wf = WatchFile::new(None);
113        assert_eq!(wf.version(), super::DEFAULT_VERSION);
114
115        assert_eq!("", wf.to_string());
116
117        let wf = WatchFile::new(Some(4));
118        assert_eq!(wf.version(), 4);
119
120        assert_eq!("version=4\n", wf.to_string());
121    }
122
123    #[test]
124    fn test_set_version() {
125        let mut wf = WatchFile::new(Some(4));
126        assert_eq!(wf.version(), 4);
127
128        wf.set_version(5);
129        assert_eq!(wf.version(), 5);
130        assert_eq!("version=5\n", wf.to_string());
131
132        // Test setting version on a file without version
133        let mut wf = WatchFile::new(None);
134        assert_eq!(wf.version(), super::DEFAULT_VERSION);
135
136        wf.set_version(4);
137        assert_eq!(wf.version(), 4);
138        assert_eq!("version=4\n", wf.to_string());
139    }
140
141    #[test]
142    fn test_set_version_on_parsed() {
143        // Test that parsed WatchFiles can be mutated
144        let mut wf: WatchFile = "version=4\n".parse().unwrap();
145        assert_eq!(wf.version(), 4);
146
147        wf.set_version(5);
148        assert_eq!(wf.version(), 5);
149        assert_eq!("version=5\n", wf.to_string());
150
151        // Test setting version on a parsed file without version
152        let mut wf: WatchFile = "".parse().unwrap();
153        assert_eq!(wf.version(), super::DEFAULT_VERSION);
154
155        wf.set_version(4);
156        assert_eq!(wf.version(), 4);
157        assert_eq!("version=4\n", wf.to_string());
158    }
159}