4
0
Fork 0
mirror of https://github.com/Anvilcraft/modpacktools synced 2024-05-19 11:54:20 +02:00

improve docs

remove mergeTo function due to stdlib containing alternative
getModMetas now takes predicate
This commit is contained in:
LordMZTE 2020-08-15 20:49:29 +02:00
parent 552457eb0e
commit a5d5562d93
17 changed files with 132 additions and 105 deletions

View file

@ -19,13 +19,11 @@ import java.util.NoSuchElementException
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit.MICROSECONDS
//Lazy initialization will prevent objects from being initilized if not needed
//Lazy initialization will prevent objects from being initialized if not needed
val CONFIG by lazy {Config("modpacktoolsconfig.toml")}
val LOADER by lazy {CommandLoader("ley.anvil.modpacktools.commands")}
val MPJH by lazy {ModpackJsonHandler(File(CONFIG.config.pathOrException<String>("Locations/src"), "modpack.json"))}
val GSON: Gson by lazy {GsonBuilder().setPrettyPrinting().create()}
//TODO thinks term has no color support on win with edited registry. probably no big deal
val TERMC by lazy {TermColors()}
//for checking if the client has been initialized when closing it

View file

@ -4,10 +4,15 @@ import net.sourceforge.argparse4j.ArgumentParsers
import net.sourceforge.argparse4j.inf.ArgumentParser
/**
* Implement this for commands. it is meant to reduce boilerplate.
* an implementation of [ICommand] meant to reduce boilerplate.
* this automatically creates a base [ArgumentParser]
* with the [helpMessage] as description and then applies [addArgs] to it
* and uses [displayName] as the name for the command in the help message.
*
* the [name] of the command will be a converted version of the [displayName] by default
*
* @param displayName the name of this command to be displayed in the help message
* @param name the internal name of the command. will be the display name in lower case and with _ instead of spaces by default
* @param name the internal name of the command. will be the [displayName] lower case and with _ instead of spaces by default
*/
abstract class AbstractCommand
@ -24,10 +29,10 @@ constructor(
}
/**
* This will be called to add arguments to the arg parser of this command.
* This will be called to add arguments to the [ArgumentParser] of this command.
* override this to add arguments.
*
* @receiver the parser to add the args to
* @receiver the [ArgumentParser] to add the args to
*/
protected open fun ArgumentParser.addArgs() {}
}

View file

@ -7,9 +7,10 @@ import org.reflections.Reflections
import org.reflections.scanners.SubTypesScanner
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.hasAnnotation
/**
* The command loader will scan the given package for {@link ICommand} classes and add them to the command list
* The command loader will scan the given package for [ICommand] classes and add them to the command list
*
* @param pkg The package to scan for commands
*/
@ -21,7 +22,9 @@ class CommandLoader(private val pkg: String) {
companion object {
/**
* Runs a command statically.
* Parses arguments for a given [ICommand] and executes it.
* Also checks if the [ICommand] has [ICommand.needsConfig] or [ICommand.needsModpackjson] set
* and throws an exception throws an exception if invalid
*
* @param args the args to pass to the command
* @throws ConfigMissingException if the command requires a config and it is not found
@ -53,24 +56,23 @@ class CommandLoader(private val pkg: String) {
refs.getSubTypesOf(ICommand::class.java).stream()
.map {it.kotlin}
//Only annotated classes
//Cannot use it.hasAnnotation because it is experimental and requires everything to be annotated so this makes more sense
.filter {it.annotations.any {ann -> ann.annotationClass == LoadCommand::class}}
//can be object
.filter {it.hasAnnotation<LoadCommand>()}
//can be object, if so use that instead of new instance
.map {it.objectInstance ?: it}
//create new instance if it is a class, otherwise just add the current instance
.forEach {if(it is ICommand) addCommand(it) else addClass(it as KClass<out ICommand>)}
}
/**
* Creates a new instance of the given class and adds it to the command list
* Creates a new instance of the given [KClass] and adds it to the command list
*
* @param clazz the class to add
* @param clazz the [KClass] to add
* @return if it was successful
*/
fun addClass(clazz: KClass<out ICommand>) = addCommand(clazz.createInstance())
/**
* Adds a command to the command list with the name that getName() returns
* Adds a command to the command list with the name that [ICommand.name] returns
*
* @param command the command to add
* @return if it was successful
@ -83,7 +85,8 @@ class CommandLoader(private val pkg: String) {
}
/**
* Runs the given command
* Runs an [ICommand] that has been loaded by this [CommandLoader]
* given a name.
*
* @param name the name of the command to be run
* @param args the arguments passed into the command

View file

@ -1,22 +1,22 @@
package ley.anvil.modpacktools.command
data class CommandReturn private constructor(val ret: String?, val success: Boolean) {
data class CommandReturn constructor(val ret: String?, val success: Boolean) {
companion object {
/**
* Get a failed {@link CommandReturn}. This should be used if something went wrong
* Get a failed [CommandReturn]. This should be used if something went wrong
*
* @param ret the error message
* @return the {@link CommandReturn}
* @return the [CommandReturn]
*/
@JvmStatic
@JvmOverloads
fun fail(ret: String? = null) = CommandReturn(ret, false)
/**
* Get a successful {@link CommandReturn} Without a message. Use this if the command was executed successfully
* Get a successful [CommandReturn]. Use this if the command was executed successfully
*
* @param ret a return message
* @return the {@link CommandReturn}
* @return the [CommandReturn]
*/
@JvmStatic
@JvmOverloads

View file

@ -15,10 +15,14 @@ interface ICommand {
*/
fun execute(args: Namespace): CommandReturn
/**
* The [ArgumentParser] which will be used to parse the arguments for this command by the [CommandLoader]
*/
val parser: ArgumentParser
/**
* this is the name of the command
* should be lower case and separated by _
*/
val name: String
@ -29,13 +33,14 @@ interface ICommand {
get() = ""
/**
* If this command needs the config file to be present. the command will not run if this returns true and there is no config file
* If this command needs the config file to be present.
* the command should not run if this returns true and there is no config file
*/
val needsConfig: Boolean
get() = true
/**
* If this returns true, the command will not run if the modpackjson file doesnt exist
* If this returns true, the command should not run if the modpackjson file doesn't exist
*/
val needsModpackjson: Boolean
get() = true

View file

@ -1,7 +1,7 @@
package ley.anvil.modpacktools.command
/**
* Tells The {@link CommandLoader} to load this command
* Tells The [CommandLoader] to load this [ICommand]
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)

View file

@ -69,9 +69,9 @@ object CreateModlist : AbstractCommand("CreateModlist") {
val all = args.getBoolean("all")
val sorting: Comparator<MetaData> = when(args.get<Sorting>("sorting")!!) {
Sorting.NAME -> comparing<MetaData, String> {it.name}
Sorting.DESCRIPTION -> comparing<MetaData, String> {it.description?.getOrNull(0) ?: ""}
Sorting.AUTHOR -> comparing<MetaData, String> {it.contributors.keys.first()}
Sorting.NAME -> comparing {it.name ?: ""}
Sorting.DESCRIPTION -> comparing {it.description?.getOrNull(0) ?: ""}
Sorting.AUTHOR -> comparing {it.contributors.keys.first()}
}
return when(args.get<Format>("type")!!) {
@ -169,7 +169,7 @@ object CreateModlist : AbstractCommand("CreateModlist") {
}
private fun getMods(all: Boolean, sorting: Comparator<MetaData>): List<MetaData> =
MPJH.getModMetas(if(all) null else arrayOf("mod")).sortedWith(sorting)
MPJH.getModMetas {all || "mod" == it.type}.sortedWith(sorting)
enum class Format {
HTML, CSV

View file

@ -57,7 +57,7 @@ object DownloadMods : AbstractCommand("DownloadMods") {
if(args.getBoolean("all"))
File(args.get<File>("dir"), it.installer.split(':')[1])
else
args.get<File>("dir"),
args.get("dir"),
it.url,
true,
!args.getBoolean("force")

View file

@ -40,7 +40,7 @@ object Import : AbstractCommand("Import") {
println("Converting...")
MPJH.modpackJsonFile.parentFile.mkdirs()
val mpjWriter = FileWriter(MPJH.modpackJsonFile)
GSON.fromJson<ManifestJSON>(manifest.readAsJson(), ManifestJSON::class.java).toAS().write(mpjWriter)
GSON.fromJson(manifest.readAsJson(), ManifestJSON::class.java).toAS().write(mpjWriter)
mpjWriter.close()
return success("Converted sucessfully")
}

View file

@ -33,9 +33,8 @@ object ListRelations : AbstractCommand("ListRelations") {
}
override fun execute(args: Namespace): CommandReturn {
val metas = MPJH.getModMetas().sortedWith(
Comparator {a, b -> b.name?.let {a.name?.compareTo(it, true)} ?: 0}
)
val metas = MPJH.getModMetas()
.sortedWith {a, b -> b.name?.let {a.name?.compareTo(it, true)} ?: 0}
if(args.getBoolean("csv")) {
metas.forEach {

View file

@ -5,37 +5,36 @@ package ley.anvil.modpacktools.util
import java.io.PrintStream
/**
* applies all given formatters to the string representation of the object and then prints it to stdout
* applies all given [formatters] to the [toString] representation of [x] and then prints it to [System.out]
*
* @param x the object to print
* @param formatters the formatters to apply to x, they will be ran in the order they are supplied in
* @param formatters the formatters to apply to [x]. they will be ran in the order they are supplied in
*/
fun fPrint(x: Any?, vararg formatters: (String) -> String) = System.out.fPrint(x, *formatters)
/**
* applies all given formatters to the string representation of the object and then prints it
* applies all given [formatters] to the [toString] representation of [x] and then prints it
*
* @receiver the printStream to print to
* @receiver the [PrintStream] to print to
* @param x the object to print
* @param formatters the formatters to apply to x, they will be ran in the order they are supplied in
* @param formatters the formatters to apply to [x]. they will be ran in the order they are supplied in
*/
fun PrintStream.fPrint(x: Any?, vararg formatters: (String) -> String) =
this.print(formatters.fold(x.toString()) {acc, f -> f(acc)})
/**
* applies all given formatters to the string representation of the object and then prints it with a newline at the end
* applies all given [formatters] to the [toString] representation of [x] and then prints it with a newline at the end
*
* @receiver the printStream to print to
* @receiver the [PrintStream] to print to
* @param x the object to print
* @param formatters the formatters to apply to x, they will be ran in the order they are supplied in
* @param formatters the formatters to apply to [x]. they will be ran in the order they are supplied in
*/
fun PrintStream.fPrintln(x: Any?, vararg formatters: (String) -> String) = this.fPrint(x, *formatters, {"$it\n"})
/**
* applies all given formatters to the string representation of the object and then prints it to stdout
* with a newline at the end
* applies all given [formatters] to the [toString] representation of [x] and then prints it to [System.out] with a newline at the end
*
* @param x the object to print
* @param formatters the formatters to apply to x, they will be ran in the order they are supplied in
* @param formatters the formatters to apply to [x]. they will be ran in the order they are supplied in
*/
fun fPrintln(x: Any?, vararg formatters: (String) -> String) = System.out.fPrintln(x, *formatters)

View file

@ -13,10 +13,10 @@ import java.nio.file.Paths
import java.util.concurrent.CountDownLatch
/**
* Downloads all supplied urls to the given files
* Downloads all supplied [files]
*
* @param files the files to download
* @param callback the callback which will be called once a download finishes
* @param callback the callback which will be called once a file in [files] finishes downloading
*/
fun downloadFiles(
files: List<FileToDownload>,
@ -33,9 +33,9 @@ fun downloadFiles(
/**
* A file that should be downloaded
*
* @param fileOrDir the file to save to
* @param url the url to download from
* @param shouldResolveFileName if true, the fileOrDir will be treated as directory and the file name will be resolved from the URL
* @param fileOrDir the [File] to save to
* @param url the [URL] to download from
* @param shouldResolveFileName if true, the [fileOrDir] will be treated as directory and the file name will be resolved from [url]
*/
data class FileToDownload(
val fileOrDir: File,
@ -74,7 +74,7 @@ open class DownloadFileTask(
var wasSkipped = true
val outFile =
if(file.shouldResolveFileName)
file.fileOrDir mergeTo Paths.get(response.request.url.toUri().path).fileName.toFile()
file.fileOrDir.resolve(Paths.get(response.request.url.toUri().path).fileName.toFile())
else
file.fileOrDir

View file

@ -8,11 +8,19 @@ import ley.anvil.modpacktools.MPJH
import java.io.File
import java.io.FileReader
/**
* This Class is responsible for reading and parsing the [modpackJsonFile]
*
* @param modpackJsonFile the file where modpack.json is located
*/
class ModpackJsonHandler(val modpackJsonFile: File) {
//Null if no file exists
/**
* the [ASWrapper] for this [modpackJsonFile].
* null if [modpackJsonFile] does not exist
*/
val asWrapper: ASWrapper?
get() {
return if (modpackJsonFile.exists()) {
return if(modpackJsonFile.exists()) {
val reader = FileReader(modpackJsonFile)
val ret = ASWrapper(AddonscriptJSON.read(reader, AddonscriptJSON::class.java))
reader.close()
@ -20,26 +28,27 @@ class ModpackJsonHandler(val modpackJsonFile: File) {
} else null
}
fun getModMetas(types: Array<String>? = null): List<MetaData> {
/**
* returns all [MetaData]s from the [AddonscriptJSON.Relation]s in this [modpackJsonFile]
*
* @param shouldInclude a Predicate which is used to determine if
* a given [AddonscriptJSON.Relation] should be included in the list.
* will include all relations by default
* @return the list of [MetaData]s of relations in this [modpackJsonFile]
*/
@JvmOverloads
fun getModMetas(shouldInclude: (AddonscriptJSON.Relation) -> Boolean = {true}): List<MetaData> {
val asJson = MPJH.asWrapper
val mods = mutableListOf<MetaData>()
val toGet = mutableListOf<ArtifactDestination>()
for (
rel in asJson!!.defaultVersion.getRelations(
arrayOf("included"), /*TODO TILERA MAKE THIS NONSESE TAKE A PREDICATE AND NOT A LIST*/
types
)
) {
if (rel.hasLocalMeta())
for(rel in asJson!!.defaultVersion.getRelations(shouldInclude)) {
if(rel.hasLocalMeta())
mods.add(rel.localMeta)
else if (rel.hasFile() && rel.file.isArtifact)
else if(rel.hasFile() && rel.file.isArtifact)
toGet.add(rel.file.artifact)
}
mods.addAll(ASWrapper.getMetaData(toGet.toTypedArray()).values)
return mods
}
val json: AddonscriptJSON?
get() = asWrapper?.json
}

View file

@ -32,10 +32,10 @@ import kotlin.reflect.full.functions
import kotlin.reflect.jvm.isAccessible
/**
* Reads a Json File
* Reads a Json [File]
*
* @receiver the file to read from
* @return the file content as JsonObject
* @return the file content as [JsonObject]
*/
fun File.readAsJson(): JsonObject {
require(this.exists()) {"File to read doesn't exist"}
@ -44,13 +44,13 @@ fun File.readAsJson(): JsonObject {
}
/**
* sends a http post request
* sends a HTTP POST request
*
* @receiver the url to send the request to
* @param contentType what content type should be used. Example: {@code MediaType.parse("application/json; utf-8")}
* @receiver the [URL] to send the request to
* @param contentType what content type should be used.
* @param payload the payload to send
* @param additionalHeaders additional headers that should be added to the request
* @return the response as string
* @return the response
*/
@Throws(IOException::class)
fun URL.httpPostStr(payload: String, contentType: MediaType? = null, additionalHeaders: Map<String, String>? = null): Response =
@ -63,13 +63,13 @@ fun URL.httpPostStr(payload: String, contentType: MediaType? = null, additionalH
).execute()
/**
* sends a http post request
* sends a HTTP POST request
*
* @receiver the url to send the request to
* @receiver the [URL] to send the request to
* @param contentType what content type should be used. Example: `"application/json; utf-8"`
* @param payload the payload to send
* @param additionalHeaders additional headers that should be added to the request
* @return the response as string
* @return the response
*/
@Throws(IOException::class)
fun URL.httpPostStr(payload: String, contentType: String, additionalHeaders: Map<String, String>? = null): Response {
@ -81,10 +81,10 @@ fun URL.httpPostStr(payload: String, contentType: String, additionalHeaders: Map
}
/**
* Sanitizes a URL to be valid by encoding illegal chars like spaces
* Sanitizes a [URL] to be valid by url-encoding illegal chars like spaces
*
* @receiver the URL to sanitize
* @return the sanitized URL
* @receiver the [URL] to sanitize
* @return the sanitized [URL]
*/
fun URL.sanitize(): URL? =
@ -103,22 +103,13 @@ fun URL.sanitize(): URL? =
}
/**
* gets a function from the receiver and makes it accessible
* gets a [KFunction] from the receiver and makes it accessible
*
* @param name the name of the function to get
* @receiver the class to get the function from
* @param name the name of the [KFunction] to get
* @receiver the [KClass] to get the [KFunction] from
*/
fun KClass<*>.getFun(name: String): KFunction<*>? = this.functions.find {it.name == name}?.apply {isAccessible = true}
/**
* merges 2 file's basically moving [other] into a directory represented by the receiver
*
* @receiver the parent directory
* @param other the file to put into [other]
* @return the combined file
*/
infix fun File.mergeTo(other: File): File = File(this.path, other.name)
/**
* zips a directory.
*
@ -150,9 +141,9 @@ fun Path.toZip(zStream: ZipOutputStream) {
fun File.toZip(zStream: ZipOutputStream) = this.toPath().toZip(zStream)
/**
* Unzips a zip file to a given directory
* Unzips a zip [File] to a given directory
*
* @receiver the zip file to unzip
* @receiver the zip [File] to unzip
* @param outputDir the dir to unzip to
*/
fun File.unzip(outputDir: File) {
@ -168,11 +159,12 @@ fun File.unzip(outputDir: File) {
}
/**
* this makes arguments for ArgumentParsers look cleaner
* this makes arguments for [ArgumentParser]s look cleaner
*
* @receiver the parser to add the argument to
* @param names the names the argument will have
* @param block this will be called on the argument. it is for settings like help message
* @receiver the [ArgumentParser] to add the argument to
* @param names the names the [Argument] will have
* @param block this will be called on the [Argument].
* it is for settings like help message
*/
fun ArgumentParser.arg(vararg names: String, block: Argument.() -> Unit) {
addArgument(*names).block()

View file

@ -2,15 +2,25 @@ package ley.anvil.modpacktools.util.addonscript
import ley.anvil.modpacktools.TERMC
import ley.anvil.modpacktools.util.fPrintln
import ley.anvil.modpacktools.util.mergeTo
import org.apache.commons.io.FileUtils
import java.io.File
import java.io.PrintStream
/**
* This will be returned by [installFile]
*
* @param success if the file was installer successfully
* @param msg an optional message returned by [installFile]
*/
data class InstallFileSuccess(
val success: Boolean,
val msg: String? = null
) {
/**
* Prints a message of this to the [out] [PrintStream]
*
* @param out the [PrintStream] to print the message to or [System.out] if not specified
*/
@JvmOverloads
fun printf(out: PrintStream = System.out) {
if(msg != null)
@ -18,6 +28,13 @@ data class InstallFileSuccess(
}
}
/**
* This will install a file given an addonscript installer
*
* @param installer the installer to use
* @param file the file to install
* @param outDir the directory to install the [file] to
*/
fun installFile(installer: String, file: File, outDir: File): InstallFileSuccess {
when {
installer == "internal.override" -> {
@ -38,7 +55,7 @@ fun installFile(installer: String, file: File, outDir: File): InstallFileSuccess
installer.startsWith("internal.dir") -> {
val (_, dir) = installer.split(":")
FileUtils.copyFile(file, File(outDir, dir) mergeTo file)
FileUtils.copyFile(file, File(outDir, dir).resolve(file))
}
installer.startsWith("internal.zip") -> {

View file

@ -8,7 +8,7 @@ class Config(val configName: String) {
val config = readConfig()
/**
* reads the config it it exists and the default config otherwise
* reads the [configLocation] file if it exists and the default config otherwise
*
* @return the Toml object of the config file
*/
@ -21,14 +21,14 @@ class Config(val configName: String) {
}
/**
* Checks if the config file exists
* Checks if the [configLocation] file exists
*
* @return true if the config file exists
*/
val exists: Boolean get() = configLocation.exists()
/**
* Copies the Config file from the resources into the tool's folder
* Copies the Config file from the resources into the project's folder
*/
fun copyConfig() {
//copy from resources

View file

@ -15,7 +15,7 @@ class ConfigToml : Toml() {
/**
* gets a path from a config.
* when getting an Int do NOT supply int to T. instead supply Long and then convert to Int!
* when getting an [Int] do NOT supply int to [T]. instead supply [Long] and then convert to [Int]!
*
* @param T what to get from the config
* @param path the path to get from the config separated by /, . or \
@ -24,7 +24,7 @@ class ConfigToml : Toml() {
/**
* gets a path from a config.
* when getting an Int do NOT supply int to T. instead supply Long and then convert to Int!
* when getting an [Int] do NOT supply int to [T]. instead supply [Long] and then convert to [Int]!
*
* @param T what to get from the config
* @param path the path to get from the config
@ -37,8 +37,8 @@ class ConfigToml : Toml() {
}
/**
* gets a path from a config and throws a MissingConfigValueException if not found.
* when getting an Int do NOT supply int to T. instead supply Long and then convert to Int!
* gets a path from a config and throws a [MissingConfigValueException] if not found.
* when getting an [Int] do NOT supply int to [T]. instead supply [Long] and then convert to [Int]!
*
* @param T what to get from the config
* @param path the path to get from the config separated by /, . or \
@ -47,8 +47,8 @@ class ConfigToml : Toml() {
fun <T> pathOrException(path: String, message: String? = null): T = getPath(path) ?: throw MissingConfigValueException(path, message)
/**
* gets a path from a config and throws a MissingConfigValueException if not found.
* when getting an Int do NOT supply int to T. instead supply Long and then convert to Int!
* gets a path from a config and throws a [MissingConfigValueException] if not found.
* when getting an [Int] do NOT supply int to [T]. instead supply [Long] and then convert to [Int]!
*
* @param T what to get from the config
* @param path the path to get from the config