use std::{
collections::HashMap,
io::BufRead,
path::{Path, PathBuf},
};
use kondo_lib::{ProjectType, ScanOptions};
use serde::Deserialize;
pub struct UnityProject {
pub path: PathBuf,
pub name: String,
pub unity_version: String,
pub package_dependencies: HashMap<String, String>,
}
pub fn discover_projects(path: &Path) -> impl Iterator<Item = UnityProject> + '_ {
kondo_lib::scan(
&path,
&ScanOptions {
follow_symlinks: false,
same_file_system: false,
},
)
.filter_map(|entry| match entry {
Ok(project) if matches!(project.project_type, ProjectType::Unity) => Some(project),
_ => None,
})
.filter_map(|project| {
let name = project_name(&project.path).ok()??;
let unity_version = {
let fs = std::fs::File::open(project.path.join("ProjectSettings/ProjectVersion.txt"))
.ok()?;
let reader = std::io::BufReader::new(fs);
const UNITY_VERSION_PREFIX: &str = "m_EditorVersion: ";
reader.lines().find_map(|line| {
if let Ok(line) = line {
line.strip_prefix(UNITY_VERSION_PREFIX)
.map(ToOwned::to_owned)
} else {
None
}
})
}?;
let package_dependencies = {
#[derive(Deserialize)]
struct PackageManifest {
dependencies: HashMap<String, String>,
}
std::fs::read_to_string(project.path.join("Packages/manifest.json"))
.ok()
.and_then(|contents| serde_json::from_str::<PackageManifest>(&contents).ok())
.map(|manifest| manifest.dependencies)
}?;
Some(UnityProject {
name,
path: project.path,
unity_version,
package_dependencies,
})
})
}
pub fn project_name(path: &Path) -> Result<Option<String>, std::io::Error> {
let fs = std::fs::File::open(path.join("ProjectSettings/ProjectSettings.asset"))?;
let reader = std::io::BufReader::new(fs);
const PRODUCT_NAME_PREFIX: &str = " productName: ";
for line in reader.lines() {
if let Some(name) = line?.strip_prefix(PRODUCT_NAME_PREFIX) {
return Ok(Some(name.to_owned()));
}
}
Ok(None)
}