Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nim bindings to objc api on mac #54

Merged
merged 2 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 41 additions & 39 deletions src/puppy.nim
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ when defined(windows) and not defined(puppyLibcurl):
import puppy/windefs, puppy/winutils
elif defined(macosx) and not defined(puppyLibcurl):
# AppKit macOS
import puppy/machttp
import puppy/macdefs
else:
# LIBCURL Linux
import libcurl
Expand Down Expand Up @@ -305,51 +305,53 @@ proc fetch*(req: Request): Response =
discard WinHttpCloseHandle(hSession)

elif defined(macosx) and not defined(puppyLibcurl):
let macHttp = newRequest(req.verb.toUpperAscii().cstring, ($req.url).cstring, req.timeout)
let pool = NSAutoreleasePool.allocInit()

for header in req.headers:
macHttp.setHeader(header.key.cstring, header.value.cstring)
try:
let request = NSMutableURLRequest.requestWithURL(
NSURL.URLWithString(@($req.url)),
NSURLRequestReloadIgnoringLocalCacheData,
req.timeout
)

macHttp.sendSync(req.body.cstring, req.body.len)
request.setHTTPMethod(@(req.verb.toUpperAscii()))

result.code = macHttp.getCode()
for header in req.headers:
request.setValue(@(header.value), @(header.key))

if req.body.len > 0:
request.setHTTPBody(NSData.dataWithBytes(req.body[0].addr, req.body.len))

block:
var
data: pointer
len: int
macHttp.getResponseBody(data.addr, len.addr)
if len > 0:
result.body = newString(len)
copyMem(result.body[0].addr, data, len)

block:
var
data: pointer
len: int
macHttp.getResponseHeaders(data.addr, len.addr)
if len > 0:
let headers = newString(len)
copyMem(headers[0].unsafeAddr, data, len)
for headerLine in headers.split(CRLF):
let arr = headerLine.split(":", 1)
if arr.len == 2:
result.headers[arr[0].strip()] = arr[1].strip()

var error: string
block:
var
data: pointer
len: int
macHttp.getResponseError(data.addr, len.addr)
if len > 0:
error.setLen(len)
copyMem(error[0].addr, data, len)
response: NSHTTPURLResponse
error: NSError
let data = NSURLConnection.sendSynchronousRequest(
request,
response.addr,
error.addr
)

if response.int != 0:
result.code = response.statusCode

macHttp.freeRequest()
let
dictionary = response.allHeaderFields
keyEnumerator = dictionary.keyEnumerator
while true:
let key = keyEnumerator.nextObject
if key.int == 0:
break
let value = dictionary.objectForKey(key)
result.headers[$(key.NSString)] = $(value.NSString)

if error != "":
raise newException(PuppyError, error)
result.body.setLen(data.length)
copyMem(result.body[0].addr, data.bytes, result.body.len)

if error.int != 0 and error.code != NSURLErrorUserCancelledAuthentication:
raise newException(PuppyError, $error)

finally:
pool.release()

else:
type
Expand Down
211 changes: 211 additions & 0 deletions src/puppy/macdefs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
{.passL: "-framework AppKit".}

type
Class* = distinct int
ID* = distinct int
SEL* = distinct int

{.push importc, cdecl, dynlib:"libobjc.dylib".}

proc objc_msgSend(self: ID, op: SEL): ID {.varargs.}
proc objc_getClass(name: cstring): Class
proc class_getName(cls: Class): cstring
proc object_getClass(id: ID): Class
proc sel_registerName(s: cstring): SEL
proc sel_getName(sel: SEL): cstring

{.pop.}

proc `$`*(cls: Class): string =
$class_getName(cls)

proc `$`*(id: ID): string =
$object_getClass(id)

proc `$`*(sel: SEL): string =
$sel_getName(sel)

type
NSAutoreleasePool* = distinct int
NSString* = distinct int
NSData* = distinct int
NSError* = distinct int
NSDictionary* = distinct int
NSEnumerator* = distinct int
NSURL* = distinct int
NSMutableURLRequest* = distinct int
NSURLRequestCachePolicy* = distinct int
NSTimeInterval* = float64
NSURLConnection* = distinct int
NSHTTPURLResponse* = distinct int

const
NSURLRequestUseProtocolCachePolicy* = 0.NSURLRequestCachePolicy
NSURLRequestReloadIgnoringLocalCacheData* = 1.NSURLRequestCachePolicy
NSURLRequestReturnCacheDataElseLoad* = 2.NSURLRequestCachePolicy
NSURLRequestReturnCacheDataDontLoad* = 3.NSURLRequestCachePolicy
NSURLRequestReloadIgnoringLocalAndRemoteCacheData* = 4.NSURLRequestCachePolicy
NSURLRequestReloadIgnoringCacheData* = NSURLRequestReloadIgnoringLocalCacheData
NSURLRequestReloadRevalidatingCacheData* = 5.NSURLRequestCachePolicy
NSURLErrorUserCancelledAuthentication* = -1012

proc allocInit*(_: typedesc[NSAutoreleasePool]): NSAutoreleasePool =
objc_msgSend(
objc_getClass("NSAutoreleasePool".cstring).ID,
sel_registerName("new".cstring)
).NSAutoreleasePool

proc release*(pool: NSAutoreleasePool) =
discard objc_msgSend(
pool.ID,
sel_registerName("release".cstring)
)

proc `@`*(s: string): NSString =
objc_msgSend(
objc_getClass("NSString".cstring).ID,
sel_registerName("stringWithUTF8String:".cstring),
s.cstring
).NSString

proc UTF8String(s: NSString): cstring =
cast[cstring](objc_msgSend(
s.ID,
sel_registerName("UTF8String".cstring)
))

proc `$`*(s: NSString): string =
$s.UTF8String

proc localizedDescription(error: NSError): NSString =
objc_msgSend(
error.ID,
sel_registerName("localizedDescription".cstring)
).NSString

proc `$`*(error: NSError): string =
$error.localizedDescription

proc code*(error: NSError): int =
objc_msgSend(
error.ID,
sel_registerName("code".cstring)
).int

proc dataWithBytes*(_: typedesc[NSData], bytes: pointer, len: int): NSData =
objc_msgSend(
objc_getClass("NSData".cstring).ID,
sel_registerName("dataWithBytes:length:".cstring),
bytes,
len
).NSData

proc bytes*(data: NSData): pointer =
cast[pointer](objc_msgSend(
data.ID,
sel_registerName("bytes".cstring)
))

proc length*(data: NSData): int =
objc_msgSend(
data.ID,
sel_registerName("length".cstring)
).int

proc keyEnumerator*(dictionary: NSDictionary): NSEnumerator =
objc_msgSend(
dictionary.ID,
sel_registerName("keyEnumerator".cstring)
).NSEnumerator

proc objectForKey*(dictionary: NSDictionary, key: ID): ID =
objc_msgSend(
dictionary.ID,
sel_registerName("objectForKey:".cstring),
key
)

proc nextObject*(enumerator: NSEnumerator): ID =
objc_msgSend(
enumerator.ID,
sel_registerName("nextObject".cstring)
)

proc URLWithString*(_: typedesc[NSURL], url: NSString): NSURL =
objc_msgSend(
objc_getClass("NSURL".cstring).ID,
sel_registerName("URLWithString:".cstring),
url
).NSURL

proc requestWithURL*(
_: typedesc[NSMutableURLRequest],
url: NSURL,
cachePolicy: NSURLRequestCachePolicy,
timeoutInterval: NSTimeInterval
): NSMutableURLRequest =
objc_msgSend(
objc_getClass("NSMutableURLRequest".cstring).ID,
sel_registerName("requestWithURL:cachePolicy:timeoutInterval:".cstring),
url,
cachePolicy,
timeoutInterval
).NSMutableURLRequest

proc setHTTPMethod*(
request: NSMutableURLRequest,
httpMethod: NSString,
) =
discard objc_msgSend(
request.ID,
sel_registerName("setHTTPMethod:".cstring),
httpMethod
)

proc setValue*(
request: NSMutableURLRequest,
value: NSString,
field: NSString
) =
discard objc_msgSend(
request.ID,
sel_registerName("setValue:forHTTPHeaderField:".cstring),
value,
field
)

proc setHTTPBody*(
request: NSMutableURLRequest,
httpBody: NSData
) =
discard objc_msgSend(
request.ID,
sel_registerName("setHTTPBody:".cstring),
httpBody
)

proc sendSynchronousRequest*(
_: typedesc[NSURLConnection],
request: NSMutableURLRequest,
response: ptr NSHTTPURLResponse,
error: ptr NSError
): NSData =
objc_msgSend(
objc_getClass("NSURLConnection".cstring).ID,
sel_registerName("sendSynchronousRequest:returningResponse:error:".cstring),
request,
response,
error
).NSData

proc statusCode*(response: NSHTTPURLResponse): int =
objc_msgSend(
response.ID,
sel_registerName("statusCode".cstring)
).int

proc allHeaderFields*(response: NSHTTPURLResponse): NSDictionary =
objc_msgSend(
response.ID,
sel_registerName("allHeaderFields".cstring)
).NSDictionary
Loading