package_parser/pkgs/
chef.rs1use 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.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}