uniui_build 0.0.15

Builds uniui applicatins for different targets
const ARCH_LIST: &'static str = "$ARCH_LIST$";
const APP_ID: &'static str = "$APP_ID$";
const PACKAGE: &'static str = "$PACKAGE$";
const SDK_PATH: &'static str = "$SDK_PATH$";
const MIN_SDK: &'static str = "$MIN_SDK$";
const TARGET_SDK: &'static str = "$TARGET_SDK$";
const NDK_PATH: &'static str = "$NDK_PATH$";

const DEPENDENCIES: &'static str = "$DEPENDENCIES$";

const APP_NAME: &'static str = "$APP_NAME$";

const VERSION: &'static str = "$VERSION$";
const VERSION_CODE: &'static str = "$VERSION_CODE$";

const ACTIVITY_NAME: &'static str = "$ACTIVITY_NAME$";
const ACTIVITIES: &'static str = "$ACTIVITIES$";

const PROJECT_NAME: &'static str = "$PROJECT_NAME$";

const PROFILE: &'static str = "$PROFILE$";


#[derive(thiserror::Error, Debug)]
pub enum Errors {
	#[error("io error")]
	IoError(#[from] std::io::Error),

	#[error("string parse error")]
	StringParseError(#[from] std::convert::Infallible),

	#[error("Toml parse error")]
	TomlParseError(#[from] toml::de::Error),

	#[error("json parse error")]
	JsonParseError(#[from] serde_json::error::Error),
}

#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct ActivityInfo {
	pub name: String,
	pub is_launcher: bool,
}

/// Creates directory with gradle scripts and all the stuff to build APK.
///
/// Parameters
/// * dir - path to generate temporary project,
/// * dependencies - dependencies for Cargo.toml, directly inserted into [[dependencies]],
/// * arch_list - list of architectures ("\"arm\", \"x86\", \"arm64\", \"x86_64\""),
/// * app_id - unique identifier for GPlay and others,
/// * sdk_dir - path to Android SDK
/// * min_sdk - earliest release of the SDK that application can run
/// * target_sdk - target version SDK
/// * ndk_dir - path to Android NDK
/// * package - base package name (may be different from app_id or the same
/// * app_name - visible app name (solution w. localisation in progress)
/// * lib_content - will be placed directly into lib.rs for generated package (Usually
///   used for reexports for functions, must be rust language),
/// * activities: Vec<ActivityInfo> - list of activities
pub fn instantiate_for(
	dir: &str,
	dependencies: &str,
	arch_list: &str,
	app_id: &str,
	min_sdk: &str,
	target_sdk: &str,
	sdk_dir: &str,
	ndk_dir: &str,
	package: &str,
	app_name: &str,
	version: &str,
	version_code: &str,
	lib_content: &str,
	activities: Vec<ActivityInfo>,
	release_profile: bool,
) -> Result<(), Errors> {
	std::fs::create_dir_all(dir)?;

	std::fs::write(
		format!("{}/build.gradle", dir),
		std::include_str!("./templates/main_build.gradle"),
	)?;

	{
		use std::{
			fs::File,
			io::Write,
		};

		let app_gradle_dir = format!("{}/gradle/wrapper/", dir);
		std::fs::create_dir_all(&app_gradle_dir)?;

		let wrapper_dir = format!("{}/gradle-wrapper.jar", app_gradle_dir);
		let wrapper_content =
			std::include_bytes!("./templates/gradle/wrapper/gradle-wrapper.jar");
		let mut file = File::create(wrapper_dir)?;
		file.write_all(wrapper_content)?;
	}

	std::fs::write(
		format!("{}/gradle/wrapper/gradle-wrapper.properties", dir),
		std::include_str!("./templates/gradle/wrapper/gradle-wrapper.properties"),
	)?;

	std::fs::write(
		&format!("{}/gradlew", dir),
		std::include_str!("./templates/gradlew"),
	)?;

	std::fs::write(
		format!("{}/settings.gradle", dir),
		std::include_str!("./templates/settings.gradle").replace(PROJECT_NAME, package),
	)?;

	std::fs::write(
		format!("{}/gradle.properties", dir),
		std::include_str!("./templates/gradle.properties"),
	)?;

	{
		let main_gradle = format!("{}/gradlew", dir);
		let main_gradle_content = std::include_str!("./templates/gradlew");
		std::fs::write(&main_gradle, &main_gradle_content)?;
		std::fs::set_permissions(
			&main_gradle,
			std::os::unix::fs::PermissionsExt::from_mode(0o700),
		)?
	}

	std::fs::write(
		format!("{}/local.properties", dir),
		std::include_str!("./templates/local.properties")
			.replace(SDK_PATH, sdk_dir)
			.replace(NDK_PATH, ndk_dir),
	)?;

	{
		let module_path = "rust";
		let rust_dir = format!("{}/{}/src", dir, module_path);
		std::fs::create_dir_all(&rust_dir)?;

		std::fs::write(format!("{}/lib.rs", rust_dir), lib_content)?;

		std::fs::write(
			format!("{}/{}/Cargo.toml", dir, module_path),
			std::include_str!("./templates/cargo.toml")
				.replace(DEPENDENCIES, dependencies),
		)?;
	}

	{
		let app_gradle_dir = format!("{}/app/", dir);
		std::fs::create_dir_all(&app_gradle_dir)?;

		std::fs::write(
			format!("{}/build.gradle", app_gradle_dir),
			std::include_str!("./templates/app_build.gradle")
				.replace(ARCH_LIST, arch_list)
				.replace(APP_ID, app_id)
				.replace(MIN_SDK, min_sdk)
				.replace(TARGET_SDK, target_sdk)
				.replace(VERSION, version)
				.replace(VERSION_CODE, version_code)
				.replace(PROFILE, match release_profile {
					true => "release",
					false => "debug",
				}),
		)?;
	}

	{
		let activities_xml = activities
			.iter()
			.map(|a| {
				match a.is_launcher {
					true => {
						format!(
							"<activity android:name=\"{}.{}\">
						<intent-filter>
							<action android:name=\"android.intent.action.MAIN\" />

							<category android:name=\"android.intent.category.LAUNCHER\" />
						</intent-filter>
					</activity>",
							uni_components::APPLICATION_PACKAGE,
							a.name,
						)
					},
					false => {
						format!(
							"<activity android:name=\"{}.{}\"/>",
							uni_components::APPLICATION_PACKAGE,
							a.name
						)
					},
				}
			})
			.collect::<Vec<_>>()
			.join("\n");


		let app_main_dir = format!("{}/app/src/main/", dir);
		std::fs::create_dir_all(&app_main_dir)?;

		std::fs::write(
			format!("{}/AndroidManifest.xml", app_main_dir),
			std::include_str!("./templates/manifest.xml")
				.replace(PACKAGE, package)
				.replace(APP_NAME, app_name)
				.replace(ACTIVITIES, &activities_xml),
		)?;
	}

	{
		let app_values_dir = format!("{}/app/src/main/res/values/", dir);
		std::fs::create_dir_all(&app_values_dir)?;
		std::fs::write(
			format!("{}/styles.xml", app_values_dir),
			std::include_str!("./templates/styles.xml"),
		)?;
	}

	{
		let package_dir = package.clone().replace(".", "/");
		let package_dir = format!("{}/app/src/main/java/{}/", dir, package_dir);
		std::fs::create_dir_all(&package_dir)?;

		std::fs::write(
			format!("{}/Application.kt", package_dir),
			std::include_str!("./templates/application.kt").replace(PACKAGE, package),
		)?;

		for activity in activities.iter() {
			std::fs::write(
				format!("{}/app/src/main/java/{}.kt", dir, activity.name),
				std::include_str!("./templates/activity.kt")
					.replace(PACKAGE, uni_components::APPLICATION_PACKAGE)
					.replace(ACTIVITY_NAME, &activity.name),
			)?;
		}
	}

	return Ok(());
}