{"id":15293542,"url":"https://github.com/redmadrobot/autograph","last_synced_at":"2025-08-04T19:40:19.903Z","repository":{"id":63920385,"uuid":"112666425","full_name":"RedMadRobot/autograph","owner":"RedMadRobot","description":"Swift source code generation kit.","archived":false,"fork":false,"pushed_at":"2018-04-25T19:00:14.000Z","size":81,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-05-07T05:11:24.424Z","etag":null,"topics":["code-generation","ios","macos","sourcekit","swift","template"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RedMadRobot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-30T22:14:33.000Z","updated_at":"2023-11-25T10:38:10.000Z","dependencies_parsed_at":"2023-01-14T14:00:45.386Z","dependency_job_id":null,"html_url":"https://github.com/RedMadRobot/autograph","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fautograph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fautograph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fautograph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2Fautograph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RedMadRobot","download_url":"https://codeload.github.com/RedMadRobot/autograph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252817645,"owners_count":21808707,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-generation","ios","macos","sourcekit","swift","template"],"created_at":"2024-09-30T16:49:54.717Z","updated_at":"2025-05-07T05:11:31.212Z","avatar_url":"https://github.com/RedMadRobot.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n![](main-illustration.png)\n## Description\n\n**Autograph** provides instruments for building source code generation utilities (command line applications) on top of the\n[Synopsis](https://github.com/RedMadRobot/synopsis) framework.\n\n## Installation\n### Swift Package Manager dependency\n\n```swift\nPackage.Dependency.package(\n    url: \"https://github.com/RedMadRobot/autograph\",\n    from: \"1.0.0\"\n)\n```\n\n## Usage\n### Overview\n\nFirst of all, in order to build a console executable using Swift there needs to be an execution entry point, a `main.swift` file.\n\n**Autograph** uses a common approach when during the `main.swift` file execution your utility app instantiates a special\n«Application» class object and passes control flow to it:\n\n```swift\n// main.swift sample code\nimport Foundation\n\nexit(AutographApplication().run())\n```\n\nmacOS console utilities are expected to return an `Int32` code after their execution, and any code different from `0` should be\ntreated as an error, thus `AutographApplication` method `run()` returns `Int32`. The method looks pretty much like this:\n\n```swift\n// class AutographApplication { ...\n\nfunc run() -\u003e Int32 {\n    do {\n        try someDangerousOperation()\n        try someOtherDangerousOperation()\n        ...\n    } catch let error {\n        print(error)\n        return 1\n    }\n    return 0\n}\n```\n\nConsidering everything above, the entry point for you is an `AutographApplication` class.\n\n### AutographApplication class\n\nIn order to create your own utility you'll need to create your own `main.swift` file following the example above,\nand make your own `AutographApplication` subclass.\n\n`AutographApplication` provides several convenient extension points for you to complete the execution process. When the app\nruns, it goes through seven major steps:\n\n##### 1. Gather execution parameters\n\n`AutographApplication` console app supports three arguments by default:\n\n* `-help` — print help;\n* `-verbose` — print additional information during execution;\n* `-project_name [name]` — provide project name to be used in generated code; if not set, \"GEN\" is used as a default project name.\n\nAll arguments along with current working directory are aggregated in an `ExecutionParameters` instance:\n\n```swift\nclass ExecutionParameters {\n    let projectName:      String\n    let verbose:          Bool\n    let printHelp:        Bool\n    let workingDirectory: String\n}\n```\n\nAn `ExecutionParameters` instance acts like a dictionary, so that you may query it for your own arguments:\n\n```\n/*\n    ./MyUtility -verbose -my_argument value\n */\n \n let parameters: ExecutionParameters = getParameters()\n let myArgument: String              = parameters[\"-my_argument\"] ?? \"default_value\"\n```\n\nArguments without values are stored in this dictionary with an empty `String` value.\n\n##### 2. Print help\n\nWhen your app is run with a `-help` argument, the execution is interrupted, and the `AutographApplication.printHelp()` method is called.\n\nIt's the first extension point for you. You may extend this method in order to provide your own help message like this:\n\n```swift\n// class App: AutographApplication {\n\noverride func printHelp() {\n    super.printHelp()\n    print(\"\"\"\n        -input\n        Input folder with model source files.\n        If not set, current working directory is used as an input folder.\n\n        -output\n        Where to put generated files.\n        If not set, current working directory is used as an input folder.\n\n\n        \"\"\")\n}\n```\n\nDon't forget to leave an empty line after your help message.\n\n##### 3. Provide list of folders with source code files\n\n`AutographApplication` asks `provideInputFoldersList(fromParameters:)` method for a list of input folders. This method\nreturns an empty list by default.\n\nIt's the next major extension point for you. Here, you need to implement a way your utility app determines the list of input folders,\nwhence the app should search for the source code files to be analysed.\n\nYou may override this method like this:\n\n```swift\n// class App: AutographApplication {\n\noverride func provideInputFoldersList(\n    fromParameters parameters: ExecutionParameters\n) throws -\u003e [String] {\n    let input: String = parameters[\"-input\"] ?? \"\"\n    return [input]\n}\n```\n\nSuch that, you query the `ExecutionParameters` for an `-input` argument, and provide a default `\"\"` value, which stands for the\ncurrent working directory.\n\n`AutographApplication` later transforms all relative paths into absolute paths by concatenating with the current working directory,\nthus the empty string `\"\"` will result in the working directory as a default input folder.\n\nIf you think it's crucial for the execution to have an explicit `-input` argument value, you may throw an exception like this:\n\n```swift\n// class App: AutographApplication {\n\nenum ExecutionError: Error, CustomStringConvertible {\n    case noInputFolder\n    \n    var description: String {\n        switch self {\n            case .noInputFolder: return \"!!! PLEASE PROVIDE AN -input FOLDER !!!\"\n        }\n    }\n}\n\noverride func provideInputFoldersList(\n    fromParameters parameters: ExecutionParameters\n) throws -\u003e [String] {\n    guard let input: String = parameters[\"-input\"]\n    else { throw ExecutionError.noInputFolder }\n    return [input]\n}\n```\n\n##### 4. Find all *.swift files in provided input folders\n\nWhen the step #3 is complete, `AutographApplication` recursively scans input folders and their subfolders for `*.swift` files.\nThe result of this operation is a list of `URL` objects, which is then passed to the **Synopsis** framework in the step #5, see below.\n\nThere's not much you can do about this process, though there's an `open` calculated property\n`AutographApplication.fileFinder`, where you may return your own `FileFinder` subclass instance if you want, for example,\nto prohibit a recursive file search.\n\n##### 5. Make a Synopsis out of all found source code\n\nStep #5 is pretty straightforward, as it makes a `Synopsis` instance using the list of `URL` entities of source code files found in the\nprevious step.\n\nAlso, it calls `Synopsis.printToXcode()` in case your app is running in `-verbose` mode.\n\nYou can't extend or override this step.\n\n##### 6. Compose utilities\n\nA `Synopsis` instance is passed into the `AutographApplication.compose(forSynopsis:parameters:)` method, where you need\nto generate new source code. At last!\n\nThis method returns a list of `Implementation` objects, each one contains the generated source code and a file path, where this\nsource code needs to be stored:\n\n```swift\nstruct Implementation {\n    let filePath:   String\n    let sourceCode: String\n}\n```\n\nUsually, this composition process is divided into several steps.\n\nFirst, you'll need to define an output folder path. `AutographApplication` won't transform this path into absolute path, thus you\nmay use the relative one, like `\".\"`.\n\nSecond, you'll need to extract all necessary information out of the obtained `Synopsis` entity.\n\nAt last, you'll generate the actual source code.\n\nDuring each step you may throw errors in case if something went wrong. Consider using an `XcodeMessage` errors in case you want\nyour app to rant over some particular source code.\n\n```swift\n// class App: AutographApplication {\n\noverride func compose(\n    forSynopsis synopsis: Synopsis,\n    parameters: ExecutionParameters\n) throws -\u003e [Implementation] {\n    // use current directory as a default output folder:\n    let output: String = parameters[\"-output\"] ?? \".\"\n    \n    // make sure everything is annotated properly:\n    try synopsis.classes.forEach { (classDescription: ClassDescription) in\n        guard classDescription.annotations.contains(annotationName: \"model\")\n        else {\n            throw XcodeMessage(\n                declaration: classDescription.declaration,\n                message: \"[MY GENERATOR] THIS CLASS IS NOT A MODEL\"\n            )\n        }\n    }\n    \n    // my composer may also throw:\n    return try MyComposer().composeSourceCode(outOfModels: synopsis.classes)\n}\n```\n\n##### 7. Write down to disk\n\nFinally, your `Implementation` instances are being written to the hard drive.\n\nAll necessary output folders are created, if needed. Also, if there's a generated source code file already, and the source code didn't\nchange — `FileWriter` won't touch it.\n\nShall you want to adjust this process, there's an `open` calculated property `AutographApplication.fileWriter`, where you may\nreturn your own `FileWriter` subclass instance.\n\n### Log class — in development\n\nDuring the app execution through steps mentioned above, different utilities like `FileFinder` or `FileWriter` may print debug\nmessages in case the app is running in a `-verbose` mode. These utilities use the same `Log.v(message:)` class method that you\ncan override in order to redirect log messages.\n\n### Running tests\n\nUse `spm_resolve.command` to load all dependencies and `spm_generate_xcodeproj.command` to assemble an Xcode project file.\nAlso, ensure Xcode targets macOS.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredmadrobot%2Fautograph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredmadrobot%2Fautograph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredmadrobot%2Fautograph/lists"}