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(feature = "discover")]
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
52pub mod parse;
53mod subst;
54mod types;
55/// Default user agent string used for HTTP requests
56pub const DEFAULT_USER_AGENT: &str = concat!("debian-watch-rs/", env!("CARGO_PKG_VERSION"));
57
58pub use release::Release;
59pub use types::*;
60
61/// Let's start with defining all kinds of tokens and
62/// composite nodes.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
64#[allow(non_camel_case_types, missing_docs, clippy::upper_case_acronyms)]
65#[repr(u16)]
66pub(crate) enum SyntaxKind {
67    KEY = 0,
68    VALUE,
69    EQUALS,
70    QUOTE,
71    COMMA,
72    CONTINUATION,
73    NEWLINE,
74    WHITESPACE, // whitespaces is explicit
75    COMMENT,    // comments
76    ERROR,      // as well as errors
77
78    // composite nodes
79    ROOT,             // The entire file
80    VERSION,          // "version=x\n"
81    ENTRY,            // "opts=foo=blah https://foo.com/bar .*/v?(\d\S+)\.tar\.gz\n"
82    OPTS_LIST,        // "opts=foo=blah"
83    OPTION,           // "foo=blah"
84    OPTION_SEPARATOR, // "," (comma separator between options)
85    URL,              // "https://foo.com/bar"
86    MATCHING_PATTERN, // ".*/v?(\d\S+)\.tar\.gz"
87    VERSION_POLICY,   // "debian"
88    SCRIPT,           // "uupdate"
89}
90
91/// Convert our `SyntaxKind` into the rowan `SyntaxKind`.
92impl From<SyntaxKind> for rowan::SyntaxKind {
93    fn from(kind: SyntaxKind) -> Self {
94        Self(kind as u16)
95    }
96}
97
98// Only export traits - specific implementations are in their modules
99// Users access linebased types via debian_watch::linebased::WatchFile
100// Users access deb822 types via debian_watch::deb822::WatchFile
101
102#[cfg(all(feature = "deb822", feature = "linebased"))]
103pub use crate::convert::{convert_to_v5, ConversionError};
104
105#[cfg(all(test, feature = "linebased"))]
106mod tests {
107    use crate::linebased::WatchFile;
108
109    #[test]
110    fn test_create_watchfile() {
111        let wf = WatchFile::new(None);
112        assert_eq!(wf.version(), super::DEFAULT_VERSION);
113
114        assert_eq!("", wf.to_string());
115
116        let wf = WatchFile::new(Some(4));
117        assert_eq!(wf.version(), 4);
118
119        assert_eq!("version=4\n", wf.to_string());
120    }
121
122    #[test]
123    fn test_set_version() {
124        let mut wf = WatchFile::new(Some(4));
125        assert_eq!(wf.version(), 4);
126
127        wf.set_version(5);
128        assert_eq!(wf.version(), 5);
129        assert_eq!("version=5\n", wf.to_string());
130
131        // Test setting version on a file without version
132        let mut wf = WatchFile::new(None);
133        assert_eq!(wf.version(), super::DEFAULT_VERSION);
134
135        wf.set_version(4);
136        assert_eq!(wf.version(), 4);
137        assert_eq!("version=4\n", wf.to_string());
138    }
139
140    #[test]
141    fn test_set_version_on_parsed() {
142        // Test that parsed WatchFiles can be mutated
143        let mut wf: WatchFile = "version=4\n".parse().unwrap();
144        assert_eq!(wf.version(), 4);
145
146        wf.set_version(5);
147        assert_eq!(wf.version(), 5);
148        assert_eq!("version=5\n", wf.to_string());
149
150        // Test setting version on a parsed file without version
151        let mut wf: WatchFile = "".parse().unwrap();
152        assert_eq!(wf.version(), super::DEFAULT_VERSION);
153
154        wf.set_version(4);
155        assert_eq!(wf.version(), 4);
156        assert_eq!("version=4\n", wf.to_string());
157    }
158}