use crate::error::*;
use std::path::{Path, PathBuf};
use std::process::Command as ProcessCommand;
pub struct AndroidSdk {
sdk_path: PathBuf,
build_tools_path: PathBuf,
build_tools_version: String,
platforms_path: PathBuf,
platforms: Vec<u32>,
}
impl AndroidSdk {
pub fn from_env() -> Result<Self> {
let sdk_path = {
let sdk_path = std::env::var("ANDROID_SDK_ROOT")
.ok()
.or_else(|| std::env::var("ANDROID_SDK_PATH").ok())
.or_else(|| std::env::var("ANDROID_HOME").ok());
PathBuf::from(sdk_path.ok_or(AndroidError::AndroidSdkNotFound)?)
};
let build_tools_path = sdk_path.join("build-tools");
let build_tools_version = std::fs::read_dir(&build_tools_path)
.map_err(|_| Error::PathNotFound(build_tools_path.clone()))?
.filter_map(|path| path.ok())
.filter(|path| path.path().is_dir())
.filter_map(|path| path.file_name().into_string().ok())
.filter(|name| name.chars().next().unwrap().is_digit(10))
.max()
.ok_or(AndroidError::BuildToolsNotFound)?;
let platforms_path = sdk_path.join("platforms");
let platforms: Vec<u32> = std::fs::read_dir(&platforms_path)
.map_err(|_| Error::PathNotFound(platforms_path.clone()))?
.filter_map(|path| path.ok())
.filter(|path| path.path().is_dir())
.filter_map(|path| path.file_name().into_string().ok())
.filter_map(|name| {
name.strip_prefix("android-")
.and_then(|api| api.parse::<u32>().ok())
})
.collect();
if platforms.is_empty() {
return Err(AndroidError::NoPlatformsFound.into());
};
Ok(Self {
sdk_path,
build_tools_path,
build_tools_version,
platforms_path,
platforms,
})
}
pub fn sdk_path(&self) -> &Path {
&self.sdk_path
}
pub fn build_tools_path(&self) -> &Path {
&self.build_tools_path
}
pub fn build_tools_version(&self) -> &str {
&self.build_tools_version
}
pub fn platforms_path(&self) -> &Path {
&self.platforms_path
}
pub fn platforms(&self) -> &[u32] {
&self.platforms
}
pub fn build_tool(&self, tool: &str, current_dir: Option<&Path>) -> Result<ProcessCommand> {
let path = self
.build_tools_path
.join(&self.build_tools_version)
.join(tool);
if !path.exists() {
return Err(Error::CmdNotFound(tool.to_string()));
}
let mut command = ProcessCommand::new(dunce::canonicalize(path)?);
if let Some(current_dir) = current_dir {
command.current_dir(current_dir);
};
Ok(command)
}
pub fn platform_tool(&self, tool: &str) -> Result<ProcessCommand> {
let path = self.sdk_path.join("platform-tools").join(tool);
if !path.exists() {
return Err(Error::CmdNotFound(tool.to_string()));
}
Ok(ProcessCommand::new(dunce::canonicalize(path)?))
}
pub fn default_platform(&self) -> u32 {
self.platforms().iter().max().cloned().unwrap()
}
pub fn platform_dir(&self, platform: u32) -> Result<PathBuf> {
let dir = self.platforms_path.join(format!("android-{}", platform));
if !dir.exists() {
return Err(AndroidError::PlatformNotFound(platform).into());
}
Ok(dir)
}
pub fn android_jar(&self, platform: u32) -> Result<PathBuf> {
let android_jar = self.platform_dir(platform)?.join("android.jar");
if !android_jar.exists() {
return Err(Error::PathNotFound(android_jar));
}
Ok(android_jar)
}
}