{
"$type": "site.standard.document",
"canonicalUrl": "https://www.jacky.wtf//essays/2024/using-lua-with-livtet",
"description": "How Livtet's integration of Lua helps allow for more library collections to appear with little change to the core library experience.\n",
"path": "/essays/2024/using-lua-with-livtet",
"publishedAt": "2024-08-17T20:00:00.000Z",
"site": "at://did:plc:e2ctbutx6kya6si4if5ngjmm/site.standard.publication/3mniussyp2d2g",
"tags": "essay",
"textContent": "One of the goals I have with [Livtet][] is to incorporate a means of extensibility. I didn't want\nto deeply write support for things like Calibre, Goodreads or Bookwyrm because these services can\nchange their outward facing implementations on a whim, in response to a security change or the like.\nHaving an extension system felt like the best way to work around this. Also I try to imagine this\ntool replacing my use and need for [Calibre][], that's still some time off. But I do look to how it\nallows plugins to manipulate and extend its behavior. In the case of Livtet, at the time of\nwriting, plugins aren't declarable by the user in the interface, you'd have to modify the manifest\nfile with the following:\n\nThis is a list of three [library lookup definitions][1] and in the case of loading Lua extensions,\nLivtet knows to do that by looking at the payload value and wrapping that in [a contextual\nextension][2] for that plugin. I chose to share the Lua context across plug-ins to allow for plugins\nto share logic as opposed to spinning up a new one for each plugin. The way a Lua extensions\ntalks to Livtet is straight-forward: it declares an (currently undocumented) interface to the host\nand Livtet invokes some code to extract that value from it to complete an operation. For example, if\nI wanted to get the list of books across my shelves from my Bookwyrm profile, some of the Lua code looks\nlike this:\n\nLivtet provides a per-invocation global method, options, that allows some information that the\nplugin requires for operation; in this case, the Bookwyrm extension requires the profile URL\nthat it'll work with. The act of calling this from Livtet looks like the following:\n\nQuite a bit is going on here. So a run-down:\n\n We grab a safe reference to the Lua context,\n Compose the Lua script we'll evaluate for our needs,\n We determine what kind of value M.library returns and pluck items from it; this mirrors the\n [trait definition][3].\n We call it and convert each of the value returned into ones Livtet can work with.\n\nThis approach is something that's been simple to replicate. Writing out tests for this currently\nrequire hitting _actual_ remote services which slow down the tests just a tad and run the risk,\nif I ran this on every commit — for example, of being blocked. I don't think it'll happen but I'm\nknown for thinking of \"what if\". That said, these changes allow me to present these collections\nlocally with little to no effort!\n\n<figure>\n <img src=\"/images/lua-livtet-book.png\" alt=\"\"/>\n <figcaption>Showing the book <em>Doppelganger</em> from my shelf on OpenLibrary.</figcaption>\n</figure>\n\n<figure>\n <img src=\"/images/lua-livtet-library.png\" alt=\"\"/>\n <figcaption>\n All of the books I've marked on my\n <a href=\"https://openlibrary.org/people/jackyalcine/books\">OpenLibrary profile</a>\n </figcaption>\n</figure>\n\nThis is shifting my thinking about how I want libraries to show in Livtet. But that's for another\npost!\n\n[livtet]: https://man.sr.ht/jacky/livtet/\n[calibre]: https://manual.calibre-ebook.com/en/latest/\n[1]: https://git.sr.ht/jacky/livtet/tree/fe7d2e6f605d5e10d3de519a4ea70b429c386392/item/livtet-impl/src/library.rs#L262-266\n[2]: https://git.sr.ht/jacky/livtet/tree/fe7d2e6f605d5e10d3de519a4ea70b429c386392/item/livtet-extensions/src/lua.rs#L374-385\n[3]: https://git.sr.ht/jacky/livtet/tree/fe7d2e6f605d5e10d3de519a4ea70b429c386392/item/livtet-impl/src/library.rs#L118-125",
"title": "Incorporating Lua with Livtet"
}