package_parser/pkgs/dart/
pubspec.rs1use packageurl::PackageUrl;
2use crate::types::DependentPackage;
3use serde_yaml::from_reader;
4use serde_yaml::Value;
5
6use crate::error::SourcePkgError;
7use crate::pkgs::common::model::{Package, PackageManifest};
8
9use std::fs::File;
10use std::path::Path;
11
12pub struct PubSpec {}
13
14impl PubSpec {
15 pub fn new() -> Self {
16 Self {}
17 }
18
19 fn collect_dependencies(
20 dep_root: &Value,
21 scope: &'static str,
22 is_optional: bool,
23 is_runtime: bool,
24 ) -> Vec<DependentPackage> {
25 match dep_root {
26 Value::Mapping(deps) => {
27 let mut requirements = vec![];
28 for (name, version) in deps {
29 let name: String = match name {
30 Value::String(s) => s.into(),
31 _ => "".into(),
32 };
33 let version: String = match version {
34 Value::String(s) => s.into(),
35 _ => "".into(),
36 };
37 if !name.is_empty() {
38 requirements.push(DependentPackage {
39 purl: PackageUrl::new("pub", name)
40 .expect("purl arguments are invalid")
41 .to_string(),
42 requirement: version,
43 scope: scope.into(),
44 is_optional,
45 is_runtime,
46 ..Default::default()
47 });
48 }
49 }
50
51 requirements
52 }
53 _ => vec![],
54 }
55 }
56
57 fn parse(path: impl AsRef<Path>) -> Result<Package, SourcePkgError> {
58 let path = path.as_ref();
59
60 let lock_path = path.with_file_name("pubspec.lock");
61 if lock_path.exists() {
62 log::info!("Found pubspec.lock, parsing it instead of pubspec.yaml");
63
64 match super::pubspec_lock::parse(lock_path) {
65 Ok(p) => return Ok(p),
66 Err(e) => {
67 log::warn!("Failed to parse pubspec.lock: {}", e);
68 }
69 }
70 }
71
72 let mut file = File::open(path)?;
73 let root: Value = from_reader(&mut file)?;
74
75 let name = match &root["name"] {
76 Value::String(s) => s.into(),
77 _ => "".into(),
78 };
79 let version = match &root["version"] {
80 Value::String(s) => s.into(),
81 _ => "".into(),
82 };
83 let declared_license = match &root["license"] {
84 Value::String(s) => s.into(),
85 _ => "".into(),
86 };
87
88 let mut requirements =
89 Self::collect_dependencies(&root["dependencies"], "dependencies", false, true);
90
91 let mut dev_requirements =
92 Self::collect_dependencies(&root["dev_dependencies"], "dev_dependencies", true, false);
93
94 let mut env_requirements =
95 Self::collect_dependencies(&root["dev_dependencies"], "environment", false, true);
96
97 requirements.append(&mut dev_requirements);
98 requirements.append(&mut env_requirements);
99
100 let package = Package {
101 name,
102 version,
103 declared_license,
104 dependencies: requirements,
105 ..Default::default()
106 };
107
108 Ok(package)
109 }
110}
111
112#[async_trait::async_trait]
113impl PackageManifest for PubSpec {
114 fn get_name(&self) -> String {
115 "pubspec".to_string()
116 }
117
118 async fn recognize(&self, path: &Path) -> Result<Package, SourcePkgError> {
119 Self::parse(path)
120 }
121
122 fn file_name_patterns(&self) -> &'static [&'static str] {
123 &["pubspec.yaml"]
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn parse_pubspec() {
133 let filepath = Path::new(concat!(
134 env!("CARGO_MANIFEST_DIR"),
135 "/testdata/pubspec/specs/authors-pubspec.yaml"
136 ));
137
138 let p = PubSpec::parse(filepath).unwrap();
139 println!("{:?}", p);
140 }
141
142 #[test]
143 fn parse_pubspec_publish() {
144 let filepath = Path::new(concat!(
145 env!("CARGO_MANIFEST_DIR"),
146 "/testdata/pubspec/specs/publish-pubspec.yaml"
147 ));
148
149 let p = PubSpec::parse(filepath).unwrap();
150 println!("{:?}", p);
151 }
152}