Skip to main content

package_parser/pkgs/
chef.rs

1use lib_ruby_parser::{Parser, ParserOptions, ParserResult};
2
3use crate::error::SourcePkgError;
4use crate::pkgs::common::model::{Package, PackageManifest};
5
6use std::fs::File;
7use std::io::Read;
8use std::path::Path;
9
10pub struct Chef {}
11
12impl Chef {
13    pub fn new() -> Self {
14        Self {}
15    }
16
17    fn parse_send_node(
18        statement: &lib_ruby_parser::Node,
19        node_name: &'static str,
20    ) -> Option<String> {
21        match statement {
22            lib_ruby_parser::Node::Send(send_node) => {
23                // if send_node.name == "send" {
24                //     if let lib_ruby_parser::Node::Send(send_node) = send_node.args[0] {
25                //     }
26                if send_node.method_name != node_name {
27                    return None;
28                }
29                if send_node.args.is_empty() {
30                    return None;
31                }
32                let first_node = &send_node.args[0];
33                if let lib_ruby_parser::Node::Str(str_node) = first_node {
34                    return Some(String::from_utf8(str_node.value.raw.clone()).unwrap_or_default());
35                }
36
37                None
38            }
39            _ => None,
40        }
41    }
42
43    fn parse(path: impl AsRef<Path>) -> Result<Package, SourcePkgError> {
44        let mut file = File::open(path)?;
45        let mut content = vec![];
46        file.read_to_end(&mut content)?;
47        let options = ParserOptions {
48            ..Default::default()
49        };
50        let parser = Parser::new(content, options);
51        let ParserResult { ast, .. } = parser.do_parse();
52        let mut package = Package::default();
53
54        let ast = match ast {
55            Some(ast) => ast,
56            None => return Ok(package),
57        };
58
59        if let lib_ruby_parser::Node::Begin(begin) = *ast {
60            for st in begin.statements {
61                if let Some(name) = Self::parse_send_node(&st, "name") {
62                    if package.name.is_empty() {
63                        package.name = name;
64                    }
65                } else if let Some(version) = Self::parse_send_node(&st, "version") {
66                    if package.version.is_empty() {
67                        package.version = version;
68                    }
69                } else if let Some(license) = Self::parse_send_node(&st, "license") {
70                    if package.declared_license.is_empty() {
71                        package.declared_license = license;
72                    }
73                }
74            }
75        }
76
77        Ok(package)
78    }
79}
80
81#[async_trait::async_trait]
82impl PackageManifest for Chef {
83    fn get_name(&self) -> String {
84        "chef".into()
85    }
86
87    async fn recognize(&self, path: &Path) -> Result<Package, SourcePkgError> {
88        Self::parse(path)
89    }
90
91    fn file_name_patterns(&self) -> &'static [&'static str] {
92        &["metadata.rb"]
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_parse_chef_1() {
102        let filepath = Path::new(concat!(
103            env!("CARGO_MANIFEST_DIR"),
104            "/testdata/chef/dependencies/metadata.rb"
105        ));
106
107        let p = Chef::parse(filepath).unwrap();
108        println!("{:?}", p);
109    }
110}