Skip to content
Snippets Groups Projects
Commit 98268d3f authored by Hugo Hörnquist's avatar Hugo Hörnquist
Browse files

Initial commit.

parent 9ccbf00d
No related branches found
No related tags found
No related merge requests found
sqlite-to-cpp.1
sqlite-to-cpp.1: sqlite-to-cpp.1.in
sed \
-e 's/@@version@@/v0.1/g' \
-e "s/@@date@@/$$(date -I)/g" \
$< > $@
SQL_ERROR = 2
main.py 0 → 100755
#!/usr/bin/env python3
from dataclasses import dataclass
from exit_codes import SQL_ERROR
import argparse
import gettext
import hashlib
import pathlib
import sqlite3
import sys
_ = gettext.gettext
PRGR_NAME = 'sqlite-to-cpp'
def list_tables(cx):
"""Return a list of tables in database."""
cx.execute("""
SELECT name FROM sqlite_schema
WHERE name NOT LIKE 'sqlite_%'
AND type IN ('table', 'view')
""")
return [name for (name,) in cx.fetchall()]
@dataclass
class TableInfo:
"""Information about one column of one SQLite table."""
name: str
type: str
nullable: bool
default: str
def table_info(cx, table_name):
"""
Return information about each column in table.
Returns a
"""
cx.execute("""
SELECT [name], [type], [notnull], [dflt_value]
FROM pragma_table_info(?)
ORDER BY cid ASC
""",
(table_name,))
return [TableInfo(name=name,
type=type,
nullable=notnull == 0,
default=dflt_value)
for (name, type, notnull, dflt_value)
in cx.fetchall()]
def trans_name(name):
"""Transform the value of a column to something better matched with C++."""
return ''.join(n.title() for n in name.split('_'))
def trans_table(name):
"""Transform the value of a table to something better matched with C++."""
return trans_name(name)
def indent(s):
"""Prepend for spaces to string."""
return ' '*4 + s
def reflow(block):
"""
Take a multi-line string, remove indention, and return it as a list.
The first line shoud be empty. The indentation to remove is taken
from the second line.
"""
lines = block.split('\n')
if not lines:
return lines
prefix_len = 0
for c in lines[1]:
if not c.isspace():
break
prefix_len += 1
return (line[prefix_len:] for line in lines)
def enum_declaration(table, info):
"""Return a C++ enum declaration."""
return map(indent, [
f'enum {trans_table(table)} {{',
*(indent(f'{trans_name(record.name)},')
for record
in info),
f'}}; /* enum {trans_table(table)} */'])
def enum_names_declaration(table, info):
"""Return a C++ string arroy of the names of an enum."""
table = trans_table(table)
return map(indent, [
"const char* names[] = { ",
*(indent(f'"{table}::{trans_name(record.name)}",')
for record
in info),
"}; /* char* names[] */"])
def enum_out_operator_declaration(table):
"""Return a C++ operator<< definition for printing an enum."""
type = trans_table(table)
return map(indent, reflow(f"""
std::ostream& operator<< (std::ostream& out, const {type}& it) {{
out << names[it];
return out;
}} /* operator<< */
"""))
def main():
"""Entry point of program."""
gettext.bindtextdomain(PRGR_NAME, 'translation')
gettext.textdomain('translation')
parser = argparse.ArgumentParser(
prog=PRGR_NAME,
description=_('Generate C++ enums from an SQLite database.'))
parser.add_argument(
'-o', '--output',
type=pathlib.Path,
help=_('Target output file'))
parser.add_argument(
'schema',
help=_('File containing SQLite schema.'),
type=argparse.FileType('r'))
parser.add_argument(
'--header-guard',
help=_('Contents of header guard. Defaults to a hash of the file'))
parser.add_argument(
'--namespace',
default='DB',
action='store',
help=_('Name of top-level C++ namespace.'))
parser.add_argument(
'--no-namespace',
action='store_true',
help=_('Disable top level C++ namespace.'))
parser.add_argument(
'--warning-directives',
action='store_true',
help=_('Include #warning directives.'))
args = parser.parse_args()
return_value = 0
db = sqlite3.connect(':memory:')
cx = db.cursor()
cx.executescript(args.schema.read())
lines = []
for table in list_tables(cx):
try:
lines.append(f'namespace {trans_table(table)} {{')
info = table_info(cx, table)
lines.extend(enum_declaration(table, info))
# lines.append('')
# lines.extend(enum_names_declaration(table, info))
# lines.extend(enum_out_operator_declaration(table))
lines.extend([
f'}}; /* namespace {trans_table(table)} */',
''])
except sqlite3.OperationalError as e:
# TODO emit error somewhere more visibile
return_value = SQL_ERROR
# TODO sanitize e when included in the output
lines.append(f"/*\n{e}\n*/")
if args.warning_directives:
lines.append(f'#warning "{PRGR_NAME}: {e}"')
lines.append('')
body = '\n'.join(lines)
if not args.no_namespace:
body = f'namespace {args.namespace} {{\n{body}\n}}\n'
guard = args.header_guard \
or ('SHA256' + hashlib.sha256(body.encode('UTF-8')).hexdigest())
body = f"""
#ifndef {guard}
#define {guard}
{body}
#endif /* {guard} */
"""
print(body.strip(), file=args.output)
return return_value
if __name__ == '__main__':
sys.exit(main())
.TH sqlite-to-cpp 1 "@@date@@" "@@version@@"
.SH NAME
sqlite-to-cpp \- Build C++ enums from SQLite databases.
.SH SYNOPSIS
.B sqlite-to-cpp
.B [\-b \fIoutput\/\fP] [\| "other options" \|]
.IR schema-file
.SH DESCRIPTION
\" All command line arguments
.TP
\fB\-o\fR, \fB\-\-output\fR
Output file. Where to place the generated C++ code.
Defaults to standard out.
.SS Exit status
\" Exit status
.SH EXAMPLES
.SH AUTHOR
Written by Hugo Hörnquist <hugo@lysator.liu.se>.
.SH REPORTING BUGS
.sh COPYRIGHT
.sh SEE ALSO
\" Git Upstream
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment