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}