Looks very good.
FWIW, if you are curious about side-stepping Scala and the JVM etc, you can call the SQLite3 functions directly from JavaScript for Automation (JXA) by writing:
ObjC.import('sqlite3');
and then things like (MAIN only here, full snippet below):
return main(() => {
'use strict';
ObjC.import('sqlite3');
const
strPath = '~/Library/Containers/com.culturedcode.ThingsMac/' +
'Data/Library/Application\ Support/' +
'Cultured\ Code/Things/Things.sqlite3',
strSQL = 'SELECT title FROM TMTask';
const
SQLITE_OK = parseInt($.SQLITE_OK, 10),
SQLITE_ROW = parseInt($.SQLITE_ROW, 10),
ppDb = Ref();
const rows = (
bindEither(
bindEither(
$.sqlite3_open(filePath(strPath), ppDb) !== SQLITE_OK ? (
Left($.sqlite3_errmsg(ppDb[0]))
) : Right(ppDb[0]),
db => {
const ppStmt = Ref();
return $.sqlite3_prepare_v2(
db, strSQL, -1, ppStmt, Ref()
) !== SQLITE_OK ? (
Left($.sqlite3_errmsg(db))
) : Right(Tuple(db, ppStmt[0]));
}
),
tpl => unfoldr(
stmt => SQLITE_ROW === $.sqlite3_step(stmt) ? (
Just(Tuple(
$.sqlite3_column_text(stmt, 0),
stmt
))
) : Nothing(),
tpl[1]
)
)
);
return showJSON(rows);
});
})();
Yielding, for example:
[
"Add this to-do to your Today list",
"Convert a to-do into a project",
"Get Things for iPhone and iPad",
"Plan your evening",
"Create a new to-do",
"Sync your devices",
"Add to-dos from anywhere",
"Please rate the app",
"Tune your setup",
"You’re done!",
"Go step by step with checklists",
"Tag your to-dos",
"Before you go…",
"Learn the basics",
"Open multiple windows",
"Enable the Today Widget",
"Add a reminder so you won’t forget",
"Create a project",
"Make your to-dos repeat",
"Stay in the loop",
"Show your calendar events",
"Search and navigate with Quick Find",
"Hide the sidebar to focus on your work",
"Meet Things Mac",
"Plan this to-do for later",
"Any questions? We’re here to help!",
"Create a new heading",
"Double-click this to-do",
"Boost your productivity",
"Learn some keyboard shortcuts",
"Link to emails, files, and web pages",
"Alpha Project",
"Something for the Inbox"
]
[Finished in 0.093s]
Full standalone snippet for simple table reading from JXA:
(() => {
'use strict';
ObjC.import('sqlite3');
// GENERIC FUNCTIONS -----------------------------------------------------
// Just :: a -> Just a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Nothing :: () -> Nothing
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b
});
// bindEither (>>=) :: Either a -> (a -> Either b) -> Either b
const bindEither = (m, mf) =>
m.Right !== undefined ? (
mf(m.Right)
) : m;
// filePath :: String -> FilePath
const filePath = s =>
ObjC.unwrap(ObjC.wrap(s)
.stringByStandardizingPath);
// showJSON :: a -> String
const showJSON = x => JSON.stringify(x, null, 2);
// (x => Maybe [value, remainder] -> initial value -> values
// unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
const unfoldr = (f, v) => {
let xs = [];
return (
until(
mb => mb.Nothing,
mb => (
xs.push(mb.Just[0]),
f(mb.Just[1])
), Just(Tuple(v, v))
),
xs.slice(1)
);
};
// until :: (a -> Bool) -> (a -> a) -> a -> a
const until = (p, f, x) => {
let v = x;
while (!p(v)) v = f(v);
return v;
};
//------------------------------------------------------------------------
// MAIN: SIMPLE EXAMPLE OF TABLE-READING FROM JAVASCRIPT FOR AUTOMATION
const
strPath = '~/Library/Containers/com.culturedcode.ThingsMac/' +
'Data/Library/Application\ Support/' +
'Cultured\ Code/Things/Things.sqlite3',
strSQL = 'SELECT title FROM TMTask';
const
SQLITE_OK = parseInt($.SQLITE_OK, 10),
SQLITE_ROW = parseInt($.SQLITE_ROW, 10),
ppDb = Ref();
const rows = (
bindEither(
bindEither(
$.sqlite3_open(filePath(strPath), ppDb) !== SQLITE_OK ? (
Left($.sqlite3_errmsg(ppDb[0]))
) : Right(ppDb[0]),
db => {
const ppStmt = Ref();
return $.sqlite3_prepare_v2(
db, strSQL, -1, ppStmt, Ref()
) !== SQLITE_OK ? (
Left($.sqlite3_errmsg(db))
) : Right(Tuple(db, ppStmt[0]));
}
),
tpl => unfoldr(
stmt => SQLITE_ROW === $.sqlite3_step(stmt) ? (
Just(Tuple(
$.sqlite3_column_text(stmt, 0),
stmt
))
) : (
$.sqlite3_finalize(stmt),
$.sqlite3_close(tpl[0]),
Nothing()
),
tpl[1]
)
)
);
return showJSON(rows);
})();