{"id":14634630,"url":"https://github.com/flightaware/swift-tcl","last_synced_at":"2025-04-12T16:18:04.516Z","repository":{"id":66872750,"uuid":"55708162","full_name":"flightaware/swift-tcl","owner":"flightaware","description":"Swift class library that bridges between Swift and Tcl","archived":false,"fork":false,"pushed_at":"2017-09-21T15:36:28.000Z","size":233,"stargazers_count":19,"open_issues_count":0,"forks_count":3,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-04-12T16:17:46.545Z","etag":null,"topics":["swift","tcl-command","tcl-interface","tcl-interpreter"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flightaware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-07T15:57:34.000Z","updated_at":"2025-01-04T01:49:14.000Z","dependencies_parsed_at":"2023-02-27T10:46:35.999Z","dependency_job_id":null,"html_url":"https://github.com/flightaware/swift-tcl","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/flightaware%2Fswift-tcl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fswift-tcl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fswift-tcl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fswift-tcl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flightaware","download_url":"https://codeload.github.com/flightaware/swift-tcl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248594191,"owners_count":21130316,"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":["swift","tcl-command","tcl-interface","tcl-interpreter"],"created_at":"2024-09-09T19:02:00.899Z","updated_at":"2025-04-12T16:18:04.481Z","avatar_url":"https://github.com/flightaware.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"## Overview\n\nThis is Swift Tcl, a bridge between Swift and Tcl, providing deep interoperability between the two languages.\n\nSwift developers can use Tcl to make new and powerful ways to interact with, debug, construct automated testing for, and orchestrate the high level activities of their applications.\n\nTcl developers can use Swift to gets its sweet, expressive, high performance, scripting-language-like capabilities wherever they would like while retaining all of their existing code.\n\nEither can go in for a lot or a little.\n\nDevelopers can extend Tcl by writing new commands in Swift and extend Swift by writing new commands in Tcl.  Tcl commands written in Swift are tiny and simple compared to those written in C.  They are a joy.\n\nSoon, the bridge will be able to automatically generate Tcl interfaces to existing Swift functions and classes through automated scanning of Swift source.\n\nLikewise through introspection, automation and a bit of static hinting, Tcl procedures appear as native Swift functions, with argument names, default values and \"normal\" native Swift argument and return data types.\n\nUsers of either language invoke functions written in the other indistinguishably from those written in the one they're using.\n\nLikewise errors are handled naturally across both languages in both directions.  Errors thrown from Swift code called by Tcl come back as Tcl errors with proper errorInfo, errorCode, etc.  Closing the loop, uncaught errors occuring in Tcl code called from Swift are thrown back to the caller as Swift errors.\n\nSwift Tcl defines a *TclInterp* class in Swift that provides methods for creating and managing Tcl interpreters, executing Tcl code in them, etc.  Creating a Tcl interpreter and doing something with it in Swift is as easy as:\n\n```swift\nlet interp = TclInterp()\n\ninterp.rawEval(\"puts {Hello, World.}\")\n```\n\nIt also defines a *TclObj* class that can convert between Swift data types such as Int, Double, String, Swift Arrays, Sets, and Dictionaries and Tcl object representations of equivalent types.\n\nNew Tcl commands written in Swift and are far more compact and generally simpler than their counterparts in C.\n\nReal work can be done with the Tcl interpeter without using TclObj objects at all.\n\nThe TclInterp object has methods for accessing and manipulating variables, arrays, evaluating code, etc.  In the examples below a String and a Double are obtained from variables in the Tcl interpreter:\n\n```swift\nvar autoPath: String = try interp.get(variable: \"auto_path\")\nprint(\"auto_path is '\\(autoPath)'\")\n\nlet tclVersion: Double = try interp.get(variable: \"tcl_version\")\nprint(\"Tcl version is \\(tclVersion)\")\n```\n\nY'see how get just gave you the data type you asked for with no funny business?  How cool is that?  Also note how we use *try* because get will fail if the variable doesn't exist.\n\nLikewise TclInterp's get can fetch elements out of an array:\n\n```swift\nvar machine: String = try interp.get(variable: \"tcl_platform\", element: \"machine\")\n```\n\nIn this example we import Tcl's global _tcl_platform_ array into a Swift dictionary:\n\n```swift\ntry interp.rawEval(\"array get tcl_platform\")\nvar dict: [String:String] = try interp.resultObj.toDictionary()\n```\n\nWe can then access the imported Tcl array in the usual Swift way:\n\n```swift\nprint(\"Your OS is \\(dict[\"os\"]!), running version \\(dict[\"osVersion\"]!)\")\n```\n\n## Writing Tcl extensions in Swift\n\nYou can extend Tcl with new commands written in Swift.\n\nSwift functions implementing Tcl commands are invoked with arguments comprising a TclInterp object and an array of TclObj objects corresponding to the arguments to the function (although unlike with C, objv[0] is the first argument to the function, not the function itself.)\n\nBelow is a function that will average all of its arguments that are numbers and return the result:\n\nTo report errors back to Tcl, Swift functions throw them, and because of this and due to included support your function can directly return a String, Int, Double, Bool, TclObj or TclReturn without all that tedious mucking about with the interpreter result and distinct explicit return of a Tcl return code (ok, return, break, continue, error).\n\n```swift\nfunc avg (interp: TclInterp, objv: [TclObj]) -\u003e Double {\n\tvar sum = 0.0\n\tfor obj in objv {\n\t\tguard let val = obj.doubleValue else {continue}\n\t\tsum += val\n\t}\n\treturn(sum / Double(objv.count))\n}\n\ninterp.createCommand(named: \"avg\", using: avg)\n```\n\nErrors trying to convert Tcl objects to a data type such as trying to convert an alphanumeric string to a Double are thrown by the underlying helper functions and caught by Swift Tcl if you don't catch them in your Swift code.  You get nice native error messages.\n\nTclObj methods like *getDoubleArg* make it a bit easier by tacking on appropriate bits to Tcl's *errorInfo* traceback, making error messages from Swift conformant to the Tcl style.\n\n```swift\nfunc fa_latlongs_to_distance_cmd (interp: TclInterp, objv: [TclObj]) throws -\u003e Double {\n\tif (objv.count != 4) {\n\t\tthrow TclError.WrongNumArgs(nLeadingArguments: 0, message: \"lat0 lon0 lat1 lon1\")\n\t}\n\t\n\tlet lat1 = try objv[0].getDoubleArg(\"lat1\")\n\tlet lon1 = try objv[1].getDoubleArg(\"lon1\")\n\tlet lat2 = try objv[2].getDoubleArg(\"lat2\")\n\tlet lon2 = try objv[3].getDoubleArg(\"lon2\")\n\t\t\n\tlet distance = fa_latlongs_to_distance(lat1, lon1: lon1, lat2: lat2, lon2: lon2)\n\treturn distance\n}\n```\n\nIn the above example invoking `try interp.rawEval(\"puts \\\"distance from KIAH to KSEA is [fa_latlongs_to_distance  29.9844444 -95.3414444 47.4498889 -122.3117778]\\\"\")` emits **distance from KIAH to KSEA is 1874.5897193432174** while `try interp.rawEval(\"puts \\\"distance from KIAH to KSEA is [fa_latlongs_to_distance  29.9844444 -95.3414444 crash -122.3117778]\\\"\")` produces a Tcl traceback that looks like\n\n```\nexpected floating-point number but got \"crash\" while converting \"lat2\" argument\n    invoked from within\n\"fa_latlongs_to_distance  29.9844444 -95.3414444 crash -122.3117778\"\n    invoked from within\n\"puts \"distance from KIAH to KSEA is [fa_latlongs_to_distance  29.9844444 -95.3414444 crash -122.3117778]\"\"\n```\n\n## Methods of the TclInterp class\n\n* `var interp = TclInterp()`\n\nCreate a new Tcl interpreter.  You can create as many as you like.\n\n* `try interp.eval(code: String) -\u003e Type`\n\nEvaluate Tcl code in the Tcl interpreter. Return value is the result of the code, can be any of String, Int, Double, or Bool.\n\n* `try interp.RawEval(code: String)`\n\nEvaluate the code, don't return anything.\n\n* `var tclList: String = interp.list(list: [String])`\n\nSafely create a Tcl list from an array of Swift strings.\n\n* `try interp.RawEval(list: [String])`\n\nSafer version of rawEval that accepts a string array and converts it to a Tcl list before evaluating it, avoiding potential issues from trying to quote Tcl code ad-hoc in swift.\n\nYou can control whether or not error are printed by manipulating the *printErrors* variable, which currently defaults to true and will include the traceback.\n\n### Accessing the interpreter result.\n\nThere are a number of interfaces here, which will be trimmed down with experience:\n\n* `var result: String = interp.result`\n\nObtain the interpreter result as a string.\n\n* `var resultObj: TclObj = interp.resultObj`\n\nObtain the interpreter result as a TclObj object.\n\n* `interp.result = String`\n\nSet the interpreter result to be the specified string.\n\n* `interp.resultObj = TclObj`\n\nSet the interpreter result to the specified TclObj object.\n\n* `interp.setResult(Double)`\n* `interp.setResult(Int)`\n* `interp.setResult(Bool)`\n\nSet the interpreter result to the specified Double, Int, or Bool, respectively.\n\n* `interp.getResult() -\u003e String`\n* `interp.getResult() -\u003e Double`\n* `interp.getResult() -\u003e Int`\n* `interp.getResult() -\u003e Bool`\n\nGet the Interpreter result as the corresponding type.\n\n### Registering commands\n\n* `interp.createCommand(named: String, using: SwiftTclFuncType)`\n\nCreate a new command in the Tcl interpreter with the specified name: when the name is invoked from Tcl the corresponding Swift function will be invoked to perform the command. The Swift function should be of type (tclInterp, [TclObj]) -\u003e Type, where Type can be String, Double, Int, or Bool. Eg:\n\n* `func swiftFunction (interp: TclInterp, objv: [TclObj]) throws -\u003e Type`\n\n### Handling variables.\n\n* `var val: UnsafeMutablePointer\u003cTclObj\u003e = interp.get(variable: String, element: String?, flags: VariableFlags = [])`\n\nGet a variable or array element out of the Tcl interpreter and return it as a Tcl\\_Obj \\*.  This is internal and you shouldn't really ever need it.\n\n* `var val: TclObj = try interp.get(variable: String, element: String?, flags: VariableFlags = [])`\n\nGet a variable or array element out of the Tcl interpreter and return it as a string or throw an error.\n\n* `var val: Int = try interp.get(variable: String, element: String?, flags: VariableFlags = [])`\n\nGet a variable or array element out of the Tcl interpreter and return it as an Int.  An error is thrown if the object's contents aren't a valid list or if the element can't be converted to an Int.\n\n* `var val: Double = try interp.get(variable: String, element: String?, flags: VariableFlags = [])`\n* `var val: String = try interp.get(variable: String, element: String?, flags: VariableFlags = [])`\n* `var val: Bool = try interp.get(variable: String, element: String?, flags: VariableFlags = [])`\n\nGet a variable or array element out of the Tcl interpeter and return it as a Double, String or Bool et al or throw an error.\n\n* `interp.set(variable: varName: String, element: String?, value: String, flags: VariableFlags = [])`\n* `interp.set(variable: varName: String, element: String?, value: Int, flags: VariableFlags = [])`\n* `interp.set(variable: varName: String, element: String?, value: Double, flags: VariableFlags = [])`\n* `interp.set(variable: varName: String, element: String?, value: Bool, flags: VariableFlags = [])`\n* `interp.set(variable: varName: String, element: String?, value: TclObj, flags: VariableFlags = [])`\n\nSet a variable or array element in the Tcl interpeter to be the String, Int, Double, Bool or TclObj that was passed or throw an error if unable.  For instance you might be unable to set an array element when the variable name is a scalar.\n\n* `interp.set (array: String, from: [String: String], flags: VariableFlags = [])`\n* `interp.set (array: String, from: [String: Int], flags: VariableFlags = [])`\n* `interp.set (array: String, from: [String: Double], flags: VariableFlags = [])`\n\nImport a Swift Dictionary into a Tcl array.\n\nFlags are an OptionSet. Values are:\n* `.GlobalOnly         = TCL_GLOBAL_ONLY`\n* `.NamespaceOnly      = TCL_NAMESPACE_ONLY`\n* `.LeaveErroMsg       = TCL_LEAVE_ERR_MSG`\n* `.AppendValue        = TCL_APPEND_VALUE`\n* `.ListElement        = TCL_LIST_ELEMENT`\n* `.TraceReads         = TCL_TRACE_READS`\n* `.TraceWrites        = TCL_TRACE_WRITES`\n* `.TraceUnsets        = TCL_TRACE_UNSETS`\n* `.TraceDestroyed     = TCL_TRACE_DESTROYED`\n* `.InterpDestroyed    = TCL_INTERP_DESTROYED`\n* `.TraceArray         = TCL_TRACE_ARRAY`\n* `.TraceResultDynamic = TCL_TRACE_RESULT_DYNAMIC`\n* `.TraceResultObject  = TCL_TRACE_RESULT_OBJECT`\n\n###String substitution\n\n* `interp.subst (String, flags: SubstFlags) -\u003e String`\n* `interp.subst (TclObj, flags: SubstFlags) -\u003e TclObj`\n\nPerform substitution on String or Tcl object in the fashion of the Tcl *subst* command, performing variable substitution, evaluating square-bracketed stuff as embedded Tcl commands and substituting their result, and performing backslash substitution and return the result.\n\nFlags are an OptionSet of one or more of [.Commands, .Variables, .Backslashes, .All].  [.All] is the default.\n \n### Errors\n\n* `setErrorCode(val: String) throws`\n    \nSet the Tcl error code\n    \n* `addErrorInfo(message: String)`\n\nAppend a message to the error information\n\n## The TclObj class\n\nThe TclObj class gives Swift access to Tcl objects.  A TclObj can be wrapped around any C-level Tcl Object (Tcl\\_Obj \\*) and its methods use to access and manipulate the object.\n\nThe object can be new or existing.  For example `var obj = interp.newObj(5)` creates a new Tcl object with an integer representation and a value of 5 while `var obj = interp.resultObj` wraps an existing Tcl\\_Obj object as a Swift TclObj.\n\nThe TclObj object manages Tcl reference counts so that all this will work.  For example, setting a Tcl array element to a TclObj using `interp.set(variable: arrayName, element: element, value: obj)`, the element will continue to hold the object even if the TclObj is deleted on the Swift side.\n\n* `var obj = interp.newObj()`\n* `var obj = interp.newObj(String)`\n* `var obj = interp.newObj(Int)`\n* `var obj = interp.newObj(Double)`\n* `var obj = interp.newObj(Bool)`\n\nCreate a Swift TclObj object that's empty, contains a String, an Int, Double or Bool.\n\n* `var obj = interp.newObj(Set\u003cString/Int/Double/Bool\u003e)`\n\nCreate a TclObj containing a Tcl List initialized from a Set\n\n* `var obj = interp.newObj([String/Int/Double/Bool])`\n\nCreate a TclObj containing a Tcl List initialized from an array.\n\n* `var obj = interp.newObj([String: String/Int/Double/Bool])`\n\nCreate a TclObj containing a Tcl List initialized from an dict, in [array get] format... a list of key/value pairs.\n\n* `internal var obj = interp.newObj(UnsafeMutablePointer\u003cTcl_Obj\u003e)`\n\nCreate a TclObj object encapsulating a UnsafeMutablePointer\u003cTcl_Obj\u003e aka a Tcl\\_Obj \\*.\n\nThere are setter and getter methods for all of the above types:\n\n* `obj.set(String)`\n* `obj.set(Int)`\n* `obj.set(Double)`\n* `obj.set(Bool)`\n* `obj.set(Set\u003cString\u003e)`\n* `obj.set(Set\u003cInt\u003e)`\n* `obj.set(Set\u003cDouble\u003e)`\n* `obj.set([String])`\n* `obj.set([Int])`\n* `obj.set([Double])`\n* `obj.set([String:String])`\n* `obj.set([String:Int])`\n* `obj.set([String:Double])`\n\nAssign TclObj to contain a String, Int, Double or Bool.\n\n* `var val: String = obj.get()`\n\nSet String to contain the String representation of whatever TclObj has in it\n\n* `var val: Int = try obj.get()`\n\nSet val to contain the Int representation of the TclObj, or throws an error if object cannot be represented as an Int.\n\n* `var val: Double = try obj.get()`\n* `var val: Bool = try obj.get()`\n\nSame as the above but for Double and Bool.\n\n* `var val: Set\u003cString/Int/Double/Bool\u003e = try obj.get()`\n* `var val: [String/Int/Double/Bool] = try obj.get()`\n* `var val: [String: String/Int/Double/Bool] = try obj.get()`\n\nSame as above for sets, arrays, and dicts.\n\n* `var nativeObj: UnsafeMutablePointer\u003cTcl_Obj\u003e = obj.get()`\n\nObtain a pointer to the native C Tcl object from a TclObj\n\n* `try obj.lappend(value: UnsafeMootablePointer\u003cTcl_Obj\u003e)`\n\nAppend a Tcl\\_Obj \\* to a list contained in a TclObj.\n\n* `try obj.lappend(value: Int)`\n* `try obj.lappend(value: Double)`\n* `try obj.lappend(value: String)`\n* `try obj.lappend(value: Bool)`\n* `try obj.lappend(value: TclObj)`\n\nAppend an Int, Double, String, Bool or TclObj to a list contained in a TclObj.\n\n* `try obj.lappend(array: [Int])`\n* `try obj.lappend(array: [Double])`\n* `try obj.lappend(array: [String])`\n\nAppend an array of Int, Double, or String to a list contained in a TclObj.  Each element is appended.\n\n* `var val: Int = try obj.lindex(index)`\n* `var val: Double = try obj.lindex(index)`\n* `var val: String = try obj.lindex(index)`\n* `var val: Bool = try obj.lindex(index)`\n* `var val: TclObj = try obj.lindex(index)`\n\nReturn the nth element of the obj as a list, if possible, according to the specified data type, else throws an error.\n\n* `var val: [Int] = try obj.lrange(start...end)`\n* `var val: [Double] = try obj.lrange(start...end)`\n* `var val: [String] = try obj.lrange(start...end)`\n* `var val: [Bool] = try obj.lrange(start...end)`\n\nReturn the start...end range of object as a list\n\n* `try obj.linsert(index, list: [TclObj])`\n* `try obj.linsert(index, list: [String])`\n* `try obj.linsert(index, list: [Int])`\n* `try obj.linsert(index, list: [Double])`\n* `try obj.linsert(index, list: [Bool])`\n\nInsert the array into the object, before index.\n\n* `try obj.linsert(start...end, list: [TclObj])`\n* `try obj.linsert(start...end, list: [String])`\n* `try obj.linsert(start...end, list: [Int])`\n* `try obj.linsert(start...end, list: [Double])`\n* `try obj.linsert(start...end, list: [Bool])`\n\nInsert the array into the object, replacing the specified range.\n\n* `var count: Int = try obj.llength()`\n\nReturn the number of elements in the list contained in the TclObj or throws an error if the value in the TclObj cannot be represented as a list.\n\n* `var s: TclObj? = obj[index]`\n* `var s: String? = obj[index]`\n* `var s: [TclObj]? = obj[start...end]`\n* `var s: [String]? = obj[start...end]`\n\nSubscripting the object treats it as a list, exactly like lindex and lrange.\n\n* `obj[index] = String/Int/Double/Bool`\n* `obj[start...end] = [String/Int/Double/Bool]`\n\nAnd you can set elements in the object, exactly like linsert.\n\nFinally, a list is a sequence. Unfortunately, Swift doesn't support generic protocols, so it can only generate one type. Right now it's a sequence of TclObj:\n\n```swift\nvar intlist = interp.newObj([1, 2, 3, 4])\nfor element in intlist {\n    if let i: Int = try? element.get() {\n        print(\"Got '\\(i)'\")\n    }\n}\n```\n\n## The TclArray class\n\nThe TclArray convenience class gives Swift access to Tcl arrays.  A TclArray encapsulates an interpreter and an array name.\n\n* `var a = interp.newArray(name: String)`\n* `var a = interp.newArray(name: String, dict: [String: String])`\n* `var a = interp.newArray(name: String, dict: [String: TclObj])`\n* `var a = interp.newArray(name: String, string: String)`\n\nA TclArray can be converted into or out of a Swift dictionary.\n\n* `var d: [String: String] = array.get()`\n* `var d: [String: TclObj] = array.get()`\n* `array.set(d)`\n\nMembers of the array can be extracted or modified:\n\n* `var s: String = array[\"name\"]`\n* `array[\"name\"] = \"new value\"`\n\nThe array is a sequence of (String, TclObj):\n\n```swift\nfor (name, value) in array {\n    try print(\"array(\\(name)) = \\(value.get() as String)\")\n}\n```\n\n## Building\n\nRight now this is on the Mac and requires Xcode and the included Xcode project looks to Tcl as installed by macports (https://www.macports.org/) with the include file in /opt/local/include and the tcl dylib in /opt/local/lib.\n\nTo go this way make sure Xcode and macports are installed and install Tcl with \"sudo port install tcl\".\n\nThe code can also be built against the Tcl that gets installed in /usr/include, /usr/lib, etc, as installed by the Xcode command line tools if you change the targets in the project.\n\n## Running it\n\nI'm currently just running it from within Xcode.  The TclInterp and TclObj classes are defined in tcl.swift while main.swift defines a TclInterp and messes around with it a bit.\n\n## Stuff it has\n\n* A Swift TclInterp that wraps Tcl interpreters and provides methods to do stuff with them such as evaluate Tcl code, get and set the Interpreter result, etc.\n* A way to create new Tcl commands that invoke Swift functions\n* A Swift TclObj that wraps TclObj's and provides methods to do stuff with them\n* Methods to convert between Swift Lists, Sets and Dictionaries and Tcl lists, arrays and dicts\n* Additional methods of the TclInterp class to get and set Tcl variables (including array elements) and additional ways to interact with the Tcl interpreter that are useful\n\n## Stuff it needs\n\n* proc aliasing (generated scaffolding makes procs indistinguishable from other Swift functions)\n* tcl object system class aliasing\n\n## Stuff that would be nice eventually\n\n* Support for safe interpreters\n* Support for setting resource limits on interpreters using Tcl\\_Limit\\*\n\n## Stuff that would be cool if it's even possible\n\n* The ability to introspect Swift from Tcl to find objects and invoke methods on them (Swift seems to be moving away from even providing a way to do that.  I still think bridging is useful even if it can't do this.)\n\n## Stuff that might be interesting to try\n\n* linking Tcl variables to Swift variables using Tcl_LinkVar\n* Shadowing Tcl arrays in Swift\n* Tcl dictionary interface\n* lrange and stuff\n* automatically generating interfaces between The [TclObj] call trampoline and functions with native types\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflightaware%2Fswift-tcl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflightaware%2Fswift-tcl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflightaware%2Fswift-tcl/lists"}