cfn-guard 3.2.0

AWS CloudFormation Guard is an open-source general-purpose policy-as-code evaluation tool. It provides developers with a simple-to-use, yet powerful and expressive domain-specific language (DSL) to define policies and enables developers to validate JSON- or YAML- formatted structured data with those policies.
Documentation
#![allow(dead_code)]

pub mod reader;
pub mod writer;

#[derive(Debug, Clone)]
pub(crate) struct ReadCursor<'buffer> {
    line_num: usize,
    line_buffer: std::str::Lines<'buffer>,
    previous_lines: Vec<(usize, &'buffer str)>,
}

impl<'buffer> ReadCursor<'buffer> {
    pub(crate) fn new(buffer: &str) -> ReadCursor {
        ReadCursor {
            line_num: 0,
            line_buffer: buffer.lines(),
            previous_lines: Default::default(),
        }
    }

    pub(crate) fn next(&mut self) -> Option<(usize, &'buffer str)> {
        if self.line_num < self.previous_lines.len() {
            self.line_num += 1;
            return Some(self.previous_lines[self.line_num - 1]);
        }
        match self.line_buffer.next() {
            Some(line) => {
                self.line_num += 1;
                self.previous_lines.push((self.line_num, line));
                Some(self.previous_lines[self.line_num - 1])
            }
            None => None,
        }
    }

    #[cfg(test)]
    pub(crate) fn prev(&mut self) -> Option<(usize, &'buffer str)> {
        if self.line_num - 1 > 0 && !self.previous_lines.is_empty() {
            self.line_num -= 1;
            return Some(self.previous_lines[self.line_num]);
        }
        None
    }

    pub(crate) fn seek_line(&mut self, line: usize) -> Option<(usize, &'buffer str)> {
        if self.previous_lines.len() > line {
            self.line_num = line;
            return Some(self.previous_lines[self.line_num - 1]);
        }

        loop {
            match self.line_buffer.next() {
                Some(l) => {
                    self.line_num += 1;
                    self.previous_lines.push((self.line_num, l));
                    if self.line_num == line {
                        return Some(self.previous_lines[self.line_num - 1]);
                    }
                }
                None => return None,
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn read_cursor_test() {
        let resources = r#"
    Resources:
      s3:
        Type: AWS::S3::Bucket
      s3Policy:
        Type: AWS::S3::BucketPolicy
        Properties:
          PolicyDocument:
            Statement:
              Resource:
                Fn::Sub: "aws:arn:s3::${s3}""#;

        let mut cursor = ReadCursor::new(resources);
        while let Some(line) = cursor.next() {
            println!("{}.{}", line.0, line.1);
        }
        let prev = cursor.prev();
        assert!(prev.is_some());
        let prev = match prev {
            Some(p) => p,
            None => unreachable!(),
        };
        assert!(prev.1.contains("${s3}"));
        let _ = cursor.next();
        let mut lines = Vec::with_capacity(cursor.previous_lines.len());
        while let Some((line, prev)) = cursor.prev() {
            lines.insert(0, (line, prev));
            if prev.contains("s3Policy:") {
                break;
            }
        }
        for each in lines {
            println!("{}.{}", each.0, each.1);
        }
    }
}