Commit 9f5ba127 authored by Lysander Trischler's avatar Lysander Trischler

Initial commit

parents
*.sw?
*.h
*.so
__pycache__/
*.pyc
core
all: libtwtxt.so run
libtwtxt.so: libtwtxt.go
go build -buildmode=c-shared -o libtwtxt.so
clean:
rm -f libtwtxt.h libtwtxt.so
run: libtwtxt.so test_libgotwtxt.py
./test_libgotwtxt.py
module libtwtxt
go 1.16
require git.mills.io/yarnsocial/yarn v0.0.0-20210724071000-1727771047f8
This diff is collapsed.
import ctypes
import datetime
_so = ctypes.CDLL('./libtwtxt.so')
class Twter:
def __init__(self, nick=None, url=None, avatar=None, tagline=None):
self.nick = nick
self.url = url
self.avatar = avatar
self.tagline = tagline
def to_ctypes(self):
twter = _Twter()
if self.nick is not None:
twter.nick = self.nick.encode("utf-8")
if self.url is not None:
twter.url = self.url.encode("utf-8")
if self.avatar is not None:
twter.avatar = self.avatar.encode("utf-8")
if self.tagline is not None:
twter.tagline = self.tagline.encode("utf-8")
return ctypes.pointer(twter)
class _Twter(ctypes.Structure):
_fields_ = [("nick", ctypes.c_char_p),
("url", ctypes.c_char_p),
("avatar", ctypes.c_char_p),
("tagline", ctypes.c_char_p)]
def to_python(self):
twter = Twter()
if self.nick is not None:
twter.nick = self.nick.decode("utf-8")
if self.url is not None:
twter.url = self.url.decode("utf-8")
if self.avatar is not None:
twter.avatar = self.avatar.decode("utf-8")
if self.tagline is not None:
twter.tagline = self.tagline.decode("utf-8")
return twter
class Twt:
def __init__(self, twter=None, created=None, hash=None):
self.twter = twter
self.created = created
self.hash = hash
class _Twt(ctypes.Structure):
_fields_ = [("twter", ctypes.POINTER(_Twter)),
("created", ctypes.c_char_p),
("hash", ctypes.c_char_p)]
def to_python(self):
twt = Twt()
if self.twter is not None:
twt.twter = self.twter.contents.to_python()
if self.created is not None:
twt.created = datetime.datetime.fromisoformat(self.created.decode("utf-8"))
if self.hash is not None:
twt.hash = self.hash.decode("utf-8")
return twt
class TwtFile:
def __init__(self, twter=None, twts=None):
self.twter = twter
self.twts = twts
class _TwtFile(ctypes.Structure):
_fields_ = [("twter", ctypes.POINTER(_Twter)),
("twts", ctypes.POINTER(ctypes.POINTER(_Twt))),
("twts_len", ctypes.c_int)]
def to_python(self):
twtfile = TwtFile()
if self.twter is not None:
twtfile.twter = self.twter.contents.to_python()
twtfile.twts = []
for i in range(self.twts_len):
twtfile.twts.append(self.twts[i].contents.to_python())
return twtfile
class ParseFileResult(ctypes.Structure):
_fields_ = [("twtfile", ctypes.POINTER(_TwtFile)),
("error", ctypes.c_char_p)]
_so.parse_file.argtypes = [ctypes.c_char_p, ctypes.POINTER(_Twter)]
_so.parse_file.restype = ParseFileResult
_so.free_twt_file.argtypes = [ctypes.POINTER(_TwtFile)]
_so.free_twter.argtypes = [ctypes.POINTER(_Twter)]
_so.free_string.argtypes = [ctypes.c_char_p]
def parse_file(twtxt_input, twter):
result = _so.parse_file(twtxt_input.encode("utf-8"), twter.to_ctypes())
if result.error is not None:
msg = ctypes.string_at(result.error).decode("utf-8")
_so.free_string(result.error)
raise ValueError(msg)
if result.twtfile is None:
return None
return result.twtfile.contents.to_python()
#twtfile = TwtFile()
#if result.twtfile.contents.twter is not None:
# twtfile.twter = result.twtfile.contents.twter.contents.to_python()
#return twtfile
package main
/*
#include <stdlib.h>
struct twter {
char *nick;
char *url;
char *avatar;
char *tagline;
};
struct link {
char *text;
char *target;
};
struct subject {
char *text;
struct link *tag;
};
struct twt {
struct twter *twter;
char *created;
char *hash;
//struct subject *subject;
//struct twter *mentions[];
//struct link *links[];
//struct link *tags[];
};
struct twt_file {
struct twter *twter;
//*info *info;
struct twt *twts;
int twts_len;
};
*/
import "C"
import (
"git.mills.io/yarnsocial/yarn/types"
"git.mills.io/yarnsocial/yarn/types/lextwt"
"strings"
"time"
"unsafe"
)
//export parse_file
func parse_file(input *C.char, twter *C.struct_twter) (*C.struct_twt_file, *C.char) {
s := strings.NewReader(C.GoString(input))
author := types.Twter{}
if twter.nick != nil {
author.Nick = C.GoString(twter.nick)
}
if twter.url != nil {
author.URL = C.GoString(twter.url)
}
if twter.avatar != nil {
author.Avatar = C.GoString(twter.avatar)
}
if twter.tagline != nil {
author.Tagline = C.GoString(twter.tagline)
}
file, err := lextwt.ParseFile(s, author)
if err != nil {
return nil, C.CString(err.Error())
}
ptr := C.malloc(C.sizeof_struct_twt_file)
twtfile := (*C.struct_twt_file)(ptr)
twtfile.twter = convertTwter(file.Twter())
// twtsptr := C.malloc(C.size_t(len(file.Twts())) * C.sizeof_struct_twt)
twtsPtr := C.malloc(C.size_t(len(file.Twts())) * C.size_t(unsafe.Sizeof(uintptr(0))))
// convert the C array to a Go array so we can index it
a := (*[1<<30 - 1]*C.struct_twt)(twtsPtr)
for i, twt := range file.Twts() {
// a[i].twter = convertTwter(twt.Twter())
// a[i].created = C.CString(twt.Created().Format(time.RFC3339))
// a[i].hash = C.CString(twt.Hash())
a[i] = convertTwt(twt)
}
twtfile.twts = (*C.struct_twt)(twtsPtr)
twtfile.twts_len = C.int(len(file.Twts()))
return twtfile, nil
}
//export free_string
func free_string(s *C.char) {
if s != nil {
C.free(unsafe.Pointer(s))
}
}
//export free_twter
func free_twter(t *C.struct_twter) {
if t != nil {
free_string(t.nick)
free_string(t.url)
free_string(t.avatar)
free_string(t.tagline)
}
}
//export free_twt_file
func free_twt_file(t *C.struct_twt_file) {
if t != nil {
free_twter(t.twter)
}
}
func convertTwter(t types.Twter) *C.struct_twter {
ptr := C.malloc(C.sizeof_struct_twter)
twter := (*C.struct_twter)(ptr)
twter.nick = C.CString(t.Nick)
twter.url = C.CString(t.URL)
twter.avatar = C.CString(t.Avatar)
twter.tagline = C.CString(t.Tagline)
return twter
}
func convertTwt(t types.Twt) *C.struct_twt {
ptr := C.malloc(C.sizeof_struct_twt)
twt := (*C.struct_twt)(ptr)
twt.twter = convertTwter(t.Twter())
twt.created = C.CString(t.Created().Format(time.RFC3339))
twt.hash = C.CString(t.Hash())
return twt
}
func main() {}
#!/usr/bin/python3
import datetime
import libgotwtxt
import unittest
UTC_PLUS_2 = datetime.timezone(datetime.timedelta(hours=2))
class ParseFileTest(unittest.TestCase):
def test_single_line(self):
twter = libgotwtxt.Twter(nick="hugo",
url="https://example.com/~hugo/twtxt.txt",
avatar="https://example.com/~hugo/avatar.png",
tagline="Bla")
twtfile = libgotwtxt.parse_file("2021-08-02T10:27:42+02:00\tHello world.", twter)
self.assertIsNotNone(twtfile, "parse_file returned None")
self.assertTwterEqual(twter, twtfile.twter, "twter of twt file does not match")
self.assertEqual(1, len(twtfile.twts), "number of twts does not match")
self.assertTwtEqual(libgotwtxt.Twt(twter=twter,
created=datetime.datetime(2021, 8, 2, 10, 27, 42, tzinfo=UTC_PLUS_2),
hash="slrnx6a"),
twtfile.twts[0])
def test_two_lines(self):
twter = libgotwtxt.Twter(nick="hugo",
url="https://example.com/~hugo/twtxt.txt",
avatar="https://example.com/~hugo/avatar.png",
tagline="Bla")
twtfile = libgotwtxt.parse_file(
"2021-08-02T10:27:42+02:00\tHello world.\n"
"2021-08-03T09:28:45+02:00\tFoo bar eggs and spam.\n",
twter)
self.assertIsNotNone(twtfile, "parse_file returned None")
self.assertTwterEqual(twter, twtfile.twter, "twter of twt file does not match")
self.assertEqual(2, len(twtfile.twts), "number of twts does not match")
self.assertTwtEqual(libgotwtxt.Twt(twter=twter,
created=datetime.datetime(2021, 8, 2, 10, 27, 42, tzinfo=UTC_PLUS_2),
hash="slrnx6a"),
twtfile.twts[0], "first twt does not match")
self.assertTwtEqual(libgotwtxt.Twt(twter=twter,
created=datetime.datetime(2021, 8, 3, 9, 28, 45, tzinfo=UTC_PLUS_2),
hash="zm7fnka"),
twtfile.twts[1], "second twt does not match")
def msg(self, message_prefix):
"""Construct a message factory using the prefix, if present."""
def inner(message):
if message_prefix is None:
return message
return message_prefix + ": " + message
return inner
def assertTwterEqual(self, expected, actual, msg=None):
msg = self.msg(msg)
self.assertIsNotNone(actual, msg("twter is None"))
self.assertEqual(expected.nick, actual.nick, msg("twter nick does not match"))
self.assertEqual(expected.url, actual.url, msg("twter URL does not match"))
self.assertEqual(expected.avatar, actual.avatar, msg("twter avatar does not match"))
self.assertEqual(expected.tagline, actual.tagline, msg("twter tagline does not match"))
def assertTwtEqual(self, expected, actual, msg=None):
msg = self.msg(msg)
self.assertIsNotNone(actual, msg("twt is None"))
self.assertTwterEqual(expected.twter, actual.twter, msg("twter of twt does not match"))
self.assertEqual(expected.created, actual.created, msg("twt creation timestamp does not match"))
self.assertEqual(expected.hash, actual.hash, msg("twt hash does not match"))
if __name__ == "__main__":
unittest.main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment