<commit_before>//
//  AndroidCommand.swift
//  Noonian
//
//  Created by <NAME> on 11/4/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation
import Commandant
import Result

fileprivate enum SDKPaths: String {
    case android = "tools/android"
    case emulator = "tools/emulator"
    case buildTools = "build-tools"
    case platformTools = "platform-tools"
    case platforms = "platforms"
    case platformIncludeName = "android.jar"
    case packageTool = "aapt" // TODO: Need to come up with a better format for paths and tools
    case jackTool = "jack.jar"
    case zipAlignTool = "zipalign"
    case adbTool = "adb"
}

protocol AndroidCommand: CommandProtocol {
    typealias ClientError = NoonianError

    func run(_ options: Self.Options) throws
}

extension AndroidCommand {

    // TODO: Maybe derive the app name from something else, like configuration or directory name
    var appName: String {
        return "App"
    }

    // MARK: - Paths

    func androidHome() throws -> String {
        guard let androidHome = Environment().stringValue(for: EnvironmentKeys.androidHome.rawValue) else {
            throw NoonianError.androidHomeNotDefined
        }
        return androidHome
    }

    private func buildToolsBaseDir() throws -> String {
        return (try androidHome()).pathByAdding(component: SDKPaths.buildTools.rawValue)
    }

    func androidToolPath() throws -> String {
        return (try androidHome()).pathByAdding(component: SDKPaths.android.rawValue)
    }

    // TODO: Should provide an option to supply build tools via configuration file
    // This function will return the path to the versioned tools if provided
    // Otherwise it will return the latest tools installed
    func buildToolsPath(toolsVersion: String? = nil) throws -> String {
        let baseDir = try buildToolsBaseDir()

        if let toolsVersion = toolsVersion {
            return baseDir.pathByAdding(component: toolsVersion)
        }

        let contents = try FileManager.default.contentsOfDirectory(atPath: baseDir)

        if let latest = contents.sorted(by: >).first {
            print("Tools Version not supplied. Using latest.")
            print(baseDir.pathByAdding(component: latest))
            return baseDir.pathByAdding(component: latest)
        } else {
            throw NoonianError.noBuildTools
        }
    }

    func includeFor(target: String) throws -> String {
        return try androidHome()
            .pathByAdding(component: SDKPaths.platforms.rawValue)
            .pathByAdding(component: target)
            .pathByAdding(component: SDKPaths.platformIncludeName.rawValue)
    }

    func packageToolPath(buildTools: String) -> String {
        return buildTools.pathByAdding(component: SDKPaths.packageTool.rawValue)
    }

    func jackToolCommand(buildTools: String) -> String {
        let jackPath = buildTools.pathByAdding(component: SDKPaths.jackTool.rawValue)
        let command = "java -jar " + jackPath
        return command
    }

    func zipAlignToolCommand(buildTools: String) -> String {
        return buildTools.pathByAdding(component: SDKPaths.zipAlignTool.rawValue)
    }

    // MARK: - Running functions

    public func run(_ options: Self.Options) -> Result<(), NoonianError> {
        do {
            try run(options)
            return .success()
        } catch {
            return .failure((error as? NoonianError) ?? .internalError(error))
        }
    }

    func execute(commands: [ShellCommand]) throws {
        let task = CommandTask(name: verb, commands: commands)
        try execute(task: task)
    }

    func execute(task: CommandTask) throws {
        let runner = Runner()
        try runner.run(task: task)
    }

    func execute(commands: [ShellCommand], configuration: NoonianConfiguration) throws {
        let beforeTaskKey = "before_" + verb
        let afterTaskKey = "after_" + verb

        // TODO: Maybe put these in configuration
        let beforeTask = try? CommandTask(name: beforeTaskKey, configuration: configuration.value(for: beforeTaskKey))
        let afterTask = try? CommandTask(name: afterTaskKey, configuration: configuration.value(for: afterTaskKey))

        try [beforeTask, CommandTask(name: verb, commands: commands), afterTask].flatMap { $0 }.forEach(execute)
    }

    // TODO: Consider passing android home into all paths that need it
    // We could extract this all out to a android command builder
    func adbToolCommand() throws -> String {
        return try androidHome()
            .pathByAdding(component: SDKPaths.platformTools.rawValue)
            .pathByAdding(component: SDKPaths.adbTool.rawValue)
    }
}<commit_msg>Clean up path and tool names.<commit_after>//
//  AndroidCommand.swift
//  Noonian
//
//  Created by <NAME> on 11/4/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation
import Commandant
import Result

fileprivate enum Paths: String {
    case tools = "tools"
    case buildTools = "build-tools"
    case platformTools = "platform-tools"
    case platforms = "platforms"
    case platformIncludeName = "android.jar"
}

fileprivate enum Tools: String {
    case android = "android"
    case emulator = "emulator"
    case package = "aapt"
    case jack = "jack.jar"
    case zipAlign = "zipalign"
    case adb = "adb"
}

protocol AndroidCommand: CommandProtocol {
    typealias ClientError = NoonianError

    func run(_ options: Self.Options) throws
}

extension AndroidCommand {

    // TODO: Maybe derive the app name from something else, like configuration or directory name
    var appName: String {
        return "App"
    }

    // MARK: - Paths

    func androidHome() throws -> String {
        guard let androidHome = Environment().stringValue(for: EnvironmentKeys.androidHome.rawValue) else {
            throw NoonianError.androidHomeNotDefined
        }
        return androidHome
    }

    private func buildToolsBaseDir() throws -> String {
        return (try androidHome()).pathByAdding(component: Paths.buildTools.rawValue)
    }

    func androidToolPath() throws -> String {
        return try androidHome()
            .pathByAdding(component: Paths.tools.rawValue)
            .pathByAdding(component: Tools.android.rawValue)
    }

    // TODO: Should provide an option to supply build tools via configuration file
    // This function will return the path to the versioned tools if provided
    // Otherwise it will return the latest tools installed
    func buildToolsPath(toolsVersion: String? = nil) throws -> String {
        let baseDir = try buildToolsBaseDir()

        if let toolsVersion = toolsVersion {
            return baseDir.pathByAdding(component: toolsVersion)
        }

        let contents = try FileManager.default.contentsOfDirectory(atPath: baseDir)

        if let latest = contents.sorted(by: >).first {
            print("Tools Version not supplied. Using latest.")
            print(baseDir.pathByAdding(component: latest))
            return baseDir.pathByAdding(component: latest)
        } else {
            throw NoonianError.noBuildTools
        }
    }

    func includeFor(target: String) throws -> String {
        return try androidHome()
            .pathByAdding(component: Paths.platforms.rawValue)
            .pathByAdding(component: target)
            .pathByAdding(component: Paths.platformIncludeName.rawValue)
    }

    func packageToolPath(buildTools: String) -> String {
        return buildTools
            .pathByAdding(component: Tools.package.rawValue)
    }

    func jackToolCommand(buildTools: String) -> String {
        let jackPath = buildTools
            .pathByAdding(component: Tools.jack.rawValue)
        let command = "java -jar " + jackPath
        return command
    }

    func zipAlignToolCommand(buildTools: String) -> String {
        return buildTools.pathByAdding(component: Tools.zipAlign.rawValue)
    }

    // TODO: Consider passing android home into all paths that need it
    // We could extract this all out to a android command builder
    func adbToolCommand() throws -> String {
        return try androidHome()
            .pathByAdding(component: Paths.platformTools.rawValue)
            .pathByAdding(component: Tools.adb.rawValue)
    }

    // MARK: - Running functions

    public func run(_ options: Self.Options) -> Result<(), NoonianError> {
        do {
            try run(options)
            return .success()
        } catch {
            return .failure((error as? NoonianError) ?? .internalError(error))
        }
    }

    func execute(commands: [ShellCommand]) throws {
        let task = CommandTask(name: verb, commands: commands)
        try execute(task: task)
    }

    func execute(task: CommandTask) throws {
        let runner = Runner()
        try runner.run(task: task)
    }

    func execute(commands: [ShellCommand], configuration: NoonianConfiguration) throws {
        let beforeTaskKey = "before_" + verb
        let afterTaskKey = "after_" + verb

        // TODO: Maybe put these in configuration
        let beforeTask = try? CommandTask(name: beforeTaskKey, configuration: configuration.value(for: beforeTaskKey))
        let afterTask = try? CommandTask(name: afterTaskKey, configuration: configuration.value(for: afterTaskKey))

        try [beforeTask, CommandTask(name: verb, commands: commands), afterTask].flatMap { $0 }.forEach(execute)
    }
}
{'fraction_non_alphanumeric': 0.08999257136792953, 'fraction_numerical': 0.0021224663058473947, 'mean_word_length': 2.710236220472441, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1885969', 'n_tokens_mistral': 2567, 'n_tokens_neox': 2446, 'n_words': 746}