use super::{
BloatDir, EnforcePolicy, PackageManager, dir_size, refuse_if_manifest_stale,
run_command_with_timeout,
};
use anyhow::{Result, anyhow};
use std::fs;
use std::path::Path;
pub struct Dart;
impl Dart {
fn is_flutter(path: &Path) -> bool {
fs::read_to_string(path.join("pubspec.yaml"))
.map(|manifest| manifest.contains("flutter:") || manifest.contains("sdk: flutter"))
.unwrap_or(false)
}
}
impl PackageManager for Dart {
fn name(&self) -> &'static str {
"dart"
}
fn detect(&self, path: &Path) -> bool {
path.join("pubspec.yaml").exists()
}
fn bloat_dirs(&self, path: &Path) -> Vec<BloatDir> {
let tool = path.join(".dart_tool");
if !tool.is_dir() {
return Vec::new();
}
vec![BloatDir {
name: ".dart_tool".to_string(),
path: tool.clone(),
size_bytes: dir_size(&tool),
shared_bytes: 0,
}]
}
fn enforce_lockfile(&self, path: &Path, _policy: EnforcePolicy) -> Result<()> {
let lock = path.join("pubspec.lock");
let content = fs::read_to_string(&lock).map_err(|e| {
anyhow!(
"`pubspec.lock` could not be read ({e}) — without it `pub get` resolves \
afresh instead of restoring the versions being deleted."
)
})?;
if !content.contains("packages:") {
return Err(anyhow!(
"`pubspec.lock` has no `packages:` section — it is not a complete pub \
lockfile, so `.dart_tool` cannot be proven rebuildable from it."
));
}
refuse_if_manifest_stale(&path.join("pubspec.yaml"), &lock, "dart pub get")
}
fn restore(&self, path: &Path, timeout: std::time::Duration) -> Result<()> {
let program = if Self::is_flutter(path) {
"flutter"
} else {
"dart"
};
run_command_with_timeout(program, &["pub", "get"], path, timeout)
}
fn lockfiles(&self) -> &'static [&'static str] {
&["pubspec.lock"]
}
fn opt_in(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn detects_on_the_pub_manifest() {
let dir = tempdir().unwrap();
assert!(!Dart.detect(dir.path()));
fs::write(dir.path().join("pubspec.yaml"), "name: example\n").unwrap();
assert!(Dart.detect(dir.path()));
}
#[test]
fn claims_the_tool_directory_and_never_the_output_one() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join(".dart_tool")).unwrap();
fs::create_dir(dir.path().join("build")).unwrap();
let names: Vec<String> = Dart
.bloat_dirs(dir.path())
.into_iter()
.map(|b| b.name)
.collect();
assert_eq!(names, vec![".dart_tool"]);
}
#[test]
fn a_missing_or_malformed_lockfile_is_refused() {
let dir = tempdir().unwrap();
assert!(
Dart.enforce_lockfile(dir.path(), EnforcePolicy::default())
.is_err()
);
fs::write(dir.path().join("pubspec.lock"), "<<<<<<< HEAD\n").unwrap();
assert!(
Dart.enforce_lockfile(dir.path(), EnforcePolicy::default())
.is_err()
);
fs::write(
dir.path().join("pubspec.lock"),
"packages:\n http:\n version: \"1.2.0\"\n",
)
.unwrap();
assert!(
Dart.enforce_lockfile(dir.path(), EnforcePolicy::default())
.is_ok()
);
}
#[test]
fn a_flutter_app_is_told_apart_from_a_plain_dart_package() {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("pubspec.yaml"),
"name: cli\ndependencies:\n",
)
.unwrap();
assert!(!Dart::is_flutter(dir.path()));
fs::write(
dir.path().join("pubspec.yaml"),
"name: app\ndependencies:\n flutter:\n sdk: flutter\n",
)
.unwrap();
assert!(Dart::is_flutter(dir.path()));
}
#[test]
fn is_opt_in_because_the_bulk_of_the_directory_is_compiler_output() {
assert!(Dart.opt_in());
}
}