summaryrefslogtreecommitdiff
path: root/src/manifest.rs
diff options
context:
space:
mode:
authorJesper Jensen <jesper@jnsn.dev>2026-02-06 12:45:45 +0100
committerJesper Jensen <jesper@jnsn.dev>2026-02-06 12:45:45 +0100
commit6ef619e5ec2349a1598896b40f2e9ea5d0163a4f (patch)
treeee227b58a50afa72358d0bb8c3749d6833e944ec /src/manifest.rs
parent822de848fc1803dbc6032f90ef449b5b37717667 (diff)
Add tests for the parsing logic
Diffstat (limited to 'src/manifest.rs')
-rw-r--r--src/manifest.rs151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/manifest.rs b/src/manifest.rs
new file mode 100644
index 0000000..908dcbf
--- /dev/null
+++ b/src/manifest.rs
@@ -0,0 +1,151 @@
+use yaml_rust2::parser::Parser;
+use yaml_rust2::Event;
+use std::ops::Range;
+
+enum YContext {
+ InDocument,
+ InObject,
+ InSequence,
+ InValue(bool),
+}
+
+#[derive(Debug, PartialEq)]
+pub struct ManifestFile {
+ pub image_tags: Vec<Range<usize>>,
+}
+
+impl ManifestFile {
+ pub fn parse(content: &str) -> Self {
+ let mut yaml = Parser::new_from_str(content);
+
+ let mut images = vec!();
+ let mut scope = vec!();
+
+ loop {
+ let (ev, mark) = yaml.next_token().unwrap();
+ match ev {
+ Event::StreamStart => {}
+ Event::StreamEnd => { break; }
+ Event::DocumentStart => { scope.push(YContext::InDocument); }
+ Event::DocumentEnd => {
+ assert!(matches!(scope.pop().unwrap(), YContext::InDocument));
+ scope.pop_if(|x| matches!(x, YContext::InValue(_)));
+ },
+ Event::MappingStart(_, _) => { scope.push(YContext::InObject); },
+ Event::MappingEnd => {
+ assert!(matches!(scope.pop().unwrap(), YContext::InObject));
+ scope.pop_if(|x| matches!(x, YContext::InValue(_)));
+ },
+ Event::SequenceStart(_, _) => { scope.push(YContext::InSequence); },
+ Event::SequenceEnd => {
+ assert!(matches!(scope.pop().unwrap(), YContext::InSequence));
+ scope.pop_if(|x| matches!(x, YContext::InValue(_)));
+ },
+
+ Event::Scalar(ref txt, _, _, _) => {
+ let parent = scope.last().unwrap();
+ match parent {
+ YContext::InObject => {
+ // We are the key of a mapping, which means the next even is the value
+ scope.push(YContext::InValue(txt == "image"));
+ },
+ YContext::InSequence => {},
+ YContext::InValue(img) => {
+ if *img {
+ images.push(mark.index()..mark.index() + txt.len());
+ }
+ scope.pop();
+ },
+ // This should only happen for entirely empty documents
+ YContext::InDocument => assert!(txt == ""),
+ }
+ },
+ x => todo!("{:?}", x),
+ }
+ }
+
+ return Self{
+ image_tags: images,
+ };
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_single_image() {
+ let yaml = "image: nginx:1.21";
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 1);
+ assert_eq!(manifest.image_tags[0], 7..17);
+ }
+
+ #[test]
+ fn parse_multiple_images() {
+ let yaml = r#"
+containers:
+- image: app:v1
+- image: sidecar:v2
+"#;
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 2);
+ assert_eq!(manifest.image_tags[0], 22..28);
+ assert_eq!(manifest.image_tags[1], 38..48);
+ }
+
+ #[test]
+ fn parse_no_images() {
+ let yaml = "key: value";
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 0);
+ }
+
+ #[test]
+ fn parse_empty_document() {
+ let yaml = "";
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 0);
+ }
+
+ #[test]
+ fn parse_nested_object() {
+ let yaml = r#"
+spec:
+ container:
+ image: nginx:1.21
+"#;
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 1);
+ assert_eq!(manifest.image_tags[0], 31..41);
+ }
+
+ #[test]
+ fn parse_multiple_documents() {
+ let yaml = r#"
+kind: Service
+---
+image: nginx:1.21
+"#;
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 1);
+ assert_eq!(manifest.image_tags[0], 26..36);
+ }
+
+ #[test]
+ fn parse_image_with_digest() {
+ let yaml = "image: nginx@sha256:abc123";
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 1);
+ assert_eq!(manifest.image_tags[0], 7..26);
+ }
+
+ #[test]
+ fn parse_image_without_tag() {
+ let yaml = "image: nginx";
+ let manifest = ManifestFile::parse(yaml);
+ assert_eq!(manifest.image_tags.len(), 1);
+ assert_eq!(manifest.image_tags[0], 7..12);
+ }
+}