Skip to content

Commit

Permalink
Merge pull request #4 from IdeasOnCanvas/enhancement/commandLine
Browse files Browse the repository at this point in the history
Add command line target
  • Loading branch information
Patrick-Kladek authored Jan 10, 2018
2 parents bd091b2 + 8b6d844 commit fbc749b
Show file tree
Hide file tree
Showing 20 changed files with 688 additions and 103 deletions.
Binary file modified Documentation/How it works.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Documentation/Usage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 88 additions & 0 deletions Screenshot Framer CLI/ConsoleIO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright (c) 2017 Razeware LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
* distribute, sublicense, create a derivative work, and/or sell copies of the
* Software in any work that is designed, intended, or marketed for pedagogical or
* instructional purposes related to programming, coding, application development,
* or information technology. Permission for such use, copying, modification,
* merger, publication, distribution, sublicensing, creation of derivative works,
* or sale is expressly withheld.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

import Foundation


enum OutputType {
case error
case success
case standard
}


class ConsoleIO {

// swiftlint:disable:next identifier_name
func writeMessage(_ message: String, to: OutputType = .standard) {
if self.isDebuggerAttached() {
print(message)
return
}

switch to {
case .standard:
print("\u{001B}[;m\(message)")
case .success:
print("\u{001B}[0;32m\(message)\u{001B}[;m")
case .error:
fputs("\u{001B}[0;31m\(message)\u{001B}[;m\n", stderr)
}
}

func printUsage() {
let executableName = (CommandLine.arguments[0] as NSString).lastPathComponent

self.writeMessage("usage: \(executableName) -project <file>")
self.writeMessage("\toptional: -ignoreFontToBig")
self.writeMessage("")
}
}


// MARK: - Private

private extension ConsoleIO {

/**
* detects if an debugger is attacted
* code from https://stackoverflow.com/questions/33177182/detect-if-swift-app-is-being-run-from-xcode
*
* - returns: true if a debugger is attacted or false if not
*/
func isDebuggerAttached() -> Bool {
var info = kinfo_proc()
var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
var size = MemoryLayout<kinfo_proc>.stride
let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
assert(junk == 0, "sysctl failed")
return (info.kp_proc.p_flag & P_TRACED) != 0
}
}
127 changes: 127 additions & 0 deletions Screenshot Framer CLI/Controller.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//
// Controller.swift
// Screenshot Framer CLI
//
// Created by Patrick Kladek on 29.12.17.
// Copyright © 2017 Patrick Kladek. All rights reserved.
//

import Foundation


enum ExitStatus {
case noError
case wrongArguments
case readError
}


final class Controller {

// MARK: - Properties

private let arguments: [String]
private let console = ConsoleIO()
private var ignoreFontToBig = false

// MARK: - Lifecycle

init(arguments: [String]) {
self.arguments = arguments
}

// MARK: - Functions

func run() -> ExitStatus {
func parseParameterFailed() {
self.console.writeMessage("Wrong Parameters", to: .error)
self.console.printUsage()
}

self.ignoreFontToBig = self.arguments.filter { $0.lowercased() == "-ignorefonttobig" }.hasElements
guard let index = self.arguments.index(of: "-project") else { parseParameterFailed(); return .wrongArguments }
guard self.arguments.count >= index + 1 else { parseParameterFailed(); return .wrongArguments }

let fileManager = FileManager()
let documentURL = URL(fileURLWithPath: self.arguments[index + 1])

var isDir: ObjCBool = false
fileManager.fileExists(atPath: documentURL.path, isDirectory: &isDir)

let projects: [URL]
if isDir.boolValue {
guard let contentOfDirectory = try? fileManager.contentsOfDirectory(at: documentURL, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles, .skipsPackageDescendants, .skipsSubdirectoryDescendants]) else { return .readError }
projects = contentOfDirectory.filter { $0.pathExtension == "frame" }
} else {
projects = [documentURL]
}

var status: ExitStatus = .noError
for project in projects {
let exit = self.export(project: project)
if exit != .noError {
status = exit
}
}

return status
}
}


// MARK: - ExportControllerDelegate

extension Controller: ExportControllerDelegate {

func exportController(_ exportController: ExportController, didUpdateProgress progress: Double, file: String, layoutErrors: [LayoutError]) {
self.console.writeMessage("export: \(String(format: "%3.0f", progress * 100))%\t\(file)", to: self.checkedErrors(layoutErrors).hasElements ? .error : .success)
}
}


// MARK: - Private

extension Controller {

func layerStateHistory(for url: URL) -> LayerStateHistory? {
let decoder = JSONDecoder()
guard let data = try? Data(contentsOf: url) else { return nil }
guard let layerStates = try? decoder.decode([LayerState].self, from: data) else { return nil }

return LayerStateHistory(layerStates: layerStates, delegate: nil)
}

func export(project: URL) -> ExitStatus {
let fileCapsule = FileCapsule()
fileCapsule.projectURL = project.deletingLastPathComponent()

guard let layerStateHistory = self.layerStateHistory(for: project) else { return .readError }

let fileController = FileController(fileCapsule: fileCapsule)
let languageController = LanguageController(fileCapsule: fileCapsule)

let exportController = ExportController(layerStateHistory: layerStateHistory, fileController: fileController, languageController: languageController)
exportController.delegate = self

self.console.writeMessage("Project: \(project.lastPathComponent)")
let exportErrors = exportController.saveAllImages()

if self.checkedErrors(exportErrors).hasElements {
self.console.writeMessage("Something went wrong while exporting. Please check the projects for detailed information", to: .error)
self.console.writeMessage("Here are the error codes:", to: .error)
self.console.writeMessage("\(exportErrors.map { $0.rawValue }.joined(separator: "\n"))")
}

return .noError
}

func checkedErrors(_ errors: [LayoutError]) -> [LayoutError] {
var exportErrors = errors

if self.ignoreFontToBig {
exportErrors.remove(object: .fontToBig)
}

return exportErrors
}
}
13 changes: 13 additions & 0 deletions Screenshot Framer CLI/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// main.swift
// Screenshot Framer CLI
//
// Created by Patrick Kladek on 29.12.17.
// Copyright © 2017 Patrick Kladek. All rights reserved.
//

import Foundation


let controller = Controller(arguments: CommandLine.arguments)
_ = controller.run()
Loading

0 comments on commit fbc749b

Please sign in to comment.