Mac -> Linux: Everything you can do... I can do somewhat

Got the tipsy-robot-swift dev branch to run on Linux with some changes to APITizer and TrunkLink dev branches as well, much of the progress is on the APIng “for_linux_test” branch.

All of this will need to be revisited after the release of the new Swift Foundation, presumably after WWDC 2023 in June. Many of these patches will probably be deleted.

Still TODO

ServerSideEvents engine not implemented on Linux b/c currently uses the URLSession.bytes(for) async convenience function and I don’t need it for phase 1 of my posts-only bot, and I’m just going to wait until after WWDC. References to it wrapped in #if !os(Linux) checks.

Misc Headers

File headers where necessary.

Networking

Used by Linux to load URLRequest, etc.

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

UTTypes

Used by AppleOS b/c they have it.

#if canImport(UniformTypeIdentifiers)
import UniformTypeIdentifiers
#endif

Linux version has a kludge file to handle JUST what I use types for wrapped in #if !canImport(UniformTypeIdentifiers)

Extensions

Date

Date: only func ISO8601Format() -> String, not needed for APITizer, used in TrunkLine

#if os(Linux)
import Foundation

extension Date {
   func ISO8601Format() -> String {
       let formatter = ISO8601DateFormatter()
       return formatter.string(from: self)
   }
}

#endif

FileIO

Updated helper struct to deal with URL problems.

-  `initializer URL(filePath: filePath)` is still `URL(fileURLWithPath: filePath)`
- `.appending(component: folderName)` doesn't exist

I broke the pattern with this and did if statements instead of implementing the extension. If still a thing post WWDC, will refactor.

    static func makeFileURL(filePath:String) -> URL {
        //TODO: For iOS??
        //let locationToWrite = URL.documentsDirectory.appendingPathComponent("testImage", conformingTo: .png)
        #if os(Linux)
        return URL(fileURLWithPath: filePath)
        #else
        if #available(macOS 13.0, *) { //don't know how to use this with linux check?
            return URL(filePath: filePath)
        } else {
            // Fallback on earlier versions
             return URL(fileURLWithPath: filePath)
        }
        #endif
    }

URLSession

URLSession: add async convenience inits, with the exclusion of bytes (TODO if not added after WWDC)

Example:

public extension URLSession {
    func upload(for request: URLRequest, from bodyData:Data, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
        var dataTask: URLSessionDataTask?
        let onCancel = { dataTask?.cancel() }
        var rlocal = request
        rlocal.httpMethod = "POST"
        rlocal.httpBody = bodyData
        
        return try await withTaskCancellationHandler(
            operation: {
                try await withCheckedThrowingContinuation { continuation in
                    dataTask = self.dataTask(with: rlocal) { data, response, error in
                        guard let data = data, let response = response else {
                            let error = error ?? URLError(.badServerResponse)
                            return continuation.resume(throwing: error)
                        }
                        
                        continuation.resume(returning: (data, response))
                    }
                    
                    dataTask?.resume()
                }
            },
            onCancel: {
                onCancel()
            }
        )
    }
}

Glue Implementations

UTType

UTType library doesn’t not exist on Linux. Wrote BARE-MINIUM hard coded make it work for image file detection. It is a mess and not worth sharing here, but you can go see it https://github.com/carlynorama/APIng/blob/for_linux_test/Sources/APIng/Linux%2BUTType.swift

Actually… that’s an Improvement

In POSTEncoders) discovered new-to-me non-objectiveC way to detect Optional == nil for func makeDictionary(from itemToEncode:Any) -> [String:String]?. Both ways are in APIng, only the new way got moved to APITizer.

 if child.value is ExpressibleByNilLiteral  {
    switch child.value {
        case Optional<Any>.none: print("Nil!")
        default: 
            print("Not nil!")
            let (_, some) = Mirror(reflecting: child.value).children.first!
            //print(some)
            dictionary[key] = String(describing: some)
    }
//     //OLD: 
//     //https://forums.swift.org/t/calling-object-getclass-on-swift-objects/62790/4
//     let typeDescription = object_getClass(child.value)?.description() ?? "" //Not in linux.
//     if !typeDescription.contains("Null") && !typeDescription.contains("Empty") {
//         let (_, some) = Mirror(reflecting: child.value).children.first!
//         //print(some)
//         dictionary[key] = String(describing: some)
//     }
} else {
    dictionary[key] = String(describing: child.value)
}