Adding missing database for cache, missing file format and some image

logic.
This commit is contained in:
Sergiotarxz 2024-10-16 13:51:06 +02:00
parent ac40d32ef6
commit 3285ac3987
4 changed files with 273 additions and 43 deletions

112
lib/Exd/DB.pm Normal file
View File

@ -0,0 +1,112 @@
package Exd::DB;
use v5.40.0;
use strict;
use warnings;
use utf8;
use DBI;
use Path::Tiny;
use Exd::DB::Migrations;
my $dbh;
sub connect {
if ( defined $dbh ) {
return $dbh;
}
my $class = shift;
my $database = $class->database;
$dbh = DBI->connect(
"dbi:SQLite:dbname=$database",
,
undef, undef,
{
RaiseError => 1,
},
);
$class->_migrate($dbh);
return $dbh;
}
sub database($class) {
my $home = $ENV{HOME};
my $flatpak_config_dir = $ENV{XDG_CONFIG_HOME};
my $db_path = '';
{
if ($flatpak_config_dir) {
$db_path = $flatpak_config_dir . '/';
next;
}
$db_path = $home . '/' if $home;
}
$db_path .= '.exd/db.sqlite';
path($db_path)->parent->mkpath;
return $db_path;
}
sub _migrate {
my $class = shift;
my $dbh = shift;
local $dbh->{RaiseError} = 0;
local $dbh->{PrintError} = 0;
my @migrations = Exd::DB::Migrations::MIGRATIONS();
if ( $class->get_current_migration($dbh) > @migrations ) {
warn "Something happened there, wrong migration number.";
}
if ( $class->get_current_migration($dbh) >= @migrations ) {
say STDERR "Migrations already applied.";
return;
}
$class->_apply_migrations( $dbh, \@migrations );
}
sub _apply_migrations {
my $class = shift;
my $dbh = shift;
my $migrations = shift;
for (
my $i = $class->get_current_migration($dbh) ;
$i < @$migrations ;
$i++
)
{
local $dbh->{RaiseError} = 1;
my $current_migration = $migrations->[$i];
my $migration_number = $i + 1;
$class->_apply_migration( $dbh, $current_migration, $migration_number );
}
}
sub _apply_migration {
my $class = shift;
my $dbh = shift;
my $current_migration = shift;
my $migration_number = shift;
{
if (ref $current_migration eq 'CODE') {
$current_migration->($dbh);
next;
}
$dbh->do($current_migration);
}
$dbh->do( <<'EOF', undef, 'current_migration', $migration_number );
INSERT INTO options
VALUES ($1, $2)
ON CONFLICT (name) DO
UPDATE SET value = $2;
EOF
}
sub get_current_migration {
my $class = shift;
my $dbh = shift;
my $result = $dbh->selectrow_hashref( <<'EOF', undef, 'current_migration' );
select value from options where name = ?;
EOF
return int( $result->{value} // 0 );
}
1;

24
lib/Exd/DB/Migrations.pm Normal file
View File

@ -0,0 +1,24 @@
package Exd::DB::Migrations;
use v5.40.0;
use strict;
use warnings;
use utf8;
use feature 'signatures';
sub MIGRATIONS {
return (
'CREATE TABLE options (
name TEXT PRIMARY KEY,
value TEXT
);',
'CREATE TABLE cached_bluetooth_printers (
address TEXT PRIMARY KEY,
name TEXT NOT NULL,
port INTEGER NOT NULL
);',
);
}
1;

83
lib/Exd/FileFormat.pm Normal file
View File

@ -0,0 +1,83 @@
package Exd::FileFormat;
use v5.40.0;
use strict;
use warnings;
use utf8;
use Moo;
use Path::Tiny;
use Archive::Zip;
use Digest::SHA qw/sha256_hex/;
has dir => ( is => 'rw' );
sub new_tmp($class) {
my $tempdir = Path::Tiny->tempdir();
return $class->new( dir => $tempdir );
}
has _images_dir => ( is => 'lazy' );
sub _build__images_dir($self) {
my $dir = $self->dir;
my $images = $dir->child('images');
$images->mkpath;
return $images;
}
sub add_png_image( $self, $file_contents ) {
my $sha_image = sha256_hex($file_contents);
$self->_images_dir->child("$sha_image.png")->spew_raw($file_contents);
}
sub get_image( $self, $sha ) {
return $self->_images_dir->child("$sha.png");
}
sub get_script($self) {
return $self->dir->child('script.pl')->slurp_raw;
}
sub set_script( $self, $script_contents ) {
return $self->dir->child('script.pl')->spew_raw($script_contents);
}
sub to_zip($self) {
my $zip = Archive::Zip->new;
$self->dir->visit(
sub( $path, $state ) {
return if $path->is_dir;
my $path_relative = $path->relative( $self->dir );
$zip->addFile( $path, $path_relative );
},
{ recurse => 1 }
);
return $zip;
}
sub from_zip_file( $class, $zip_file ) {
my $tempdir = Path::Tiny->tempdir();
my $zip = Archive::Zip->new($zip_file);
$zip->extractTree( '.', $tempdir );
return $class->new( dir => $tempdir );
}
sub execute( $self, $printer ) {
my $script = $self->get_script;
my $sub =
eval
'use v5.40.0; use strict; use warnings; use utf8; no Carp::Always; use Cairo; use Pango;'
. $script;
if ($@) {
die $@;
}
$sub->($self, $printer);
}
sub save( $self, $output_file ) {
$self->to_zip->writeToFileNamed($output_file);
}
1;

View File

@ -21,6 +21,7 @@ use Capture::Tiny qw/capture/;
use Exd::Printer;
use Exd::DeviceToBluetooth;
use Exd::DeviceToImage;
use Exd::FileFormat;
use Exd::Gui::PrinterConfigure;
use JSON;
@ -61,6 +62,27 @@ has _read_from_parent => ( is => 'rw' );
has _write_to_parent => ( is => 'rw' );
has _write_to_script => ( is => 'rw' );
has device => ( is => 'rw' );
has _file_format => (
is => 'rw',
default => sub {
my $tmp = Exd::FileFormat->new_tmp;
say $tmp->dir;
$tmp->set_script(<<'EOF');
sub ($exd, $printer) {
$printer->print_text(
[
'hola mundo'
],
30
);
$printer->print_n_lf(2);
$printer->print;
}
EOF
return $tmp;
}
);
sub _tempdir_previews($self) {
if ( !defined $self->_tempdir_previews_guts ) {
@ -115,7 +137,7 @@ sub _run_script( $self, $device = undef, $verbose = 1 ) {
my $fh = $self->_write_to_script;
print $fh JSON::to_json(
{
script => $script,
exd_dir => ''.$self->_file_format->dir,
device => $device->serialize,
verbose => $verbose,
}
@ -144,8 +166,8 @@ sub _daemon_script_runner($self) {
my $fh = $self->_read_from_parent;
my $line = <$fh>;
my $data = JSON::from_json($line);
my ( $script, $device, $verbose ) =
$data->@{ 'script', 'device', 'verbose' };
my ( $exd_dir, $device, $verbose ) =
$data->@{ 'exd_dir', 'device', 'verbose' };
$device = $self->device_hash_to_object($device);
if ( $last_pid
&& $last_device->isa('Exd::DeviceToImage')
@ -157,7 +179,7 @@ sub _daemon_script_runner($self) {
my $new_pid = fork;
if ( !$new_pid ) {
eval {
$self->_on_run_script( $script, $device, $write_to_parent,
$self->_on_run_script( path($exd_dir), $device, $write_to_parent,
$verbose );
};
if ($@) {
@ -193,20 +215,15 @@ sub device_hash_to_object( $self, $device_hash ) {
return $sub->();
}
sub _on_run_script( $self, $script, $device, $write_to_parent, $verbose = 1 ) {
sub _on_run_script( $self, $exd_dir, $device, $write_to_parent, $verbose = 1 ) {
my $printer = Exd::Printer->new( device => $device );
local $| = 1;
my $exd = Exd::FileFormat->new(dir => $exd_dir);
my ( $stdout, $stderr, $exit ) = capture {
eval {
my $sub =
eval
'use v5.40.0; use strict; use warnings; use utf8; no Carp::Always; use Cairo; use Pango;'
. $script;
if ($@) {
die $@;
}
$sub->($printer);
$exd->execute($printer);
};
if ($@) {
if ($verbose) {
@ -274,25 +291,19 @@ sub _populate_editor($self, $box_editor_preview) {
$editor->set_smart_backspace(1);
$editor->set_show_line_numbers(1);
my $buffer = $editor->get_buffer();
$buffer->set_text( <<'EOF', -1 );
sub ($printer) {
$printer->print_text(
[
'hola mundo'
],
30
);
$printer->print_n_lf(2);
$printer->print;
}
EOF
$buffer->set_text( $self->_file_format->get_script, -1 );
$buffer->set_language(
Gtk4::Source::LanguageManager::get_default()->get_language('perl') );
$buffer->set_highlight_syntax(1);
$buffer->signal_connect(
'changed',
sub {
my $buffer = $self->_editor->get_buffer;
my $begin_iter = $buffer->get_iter_at_offset(0);
my $end_iter = $buffer->get_end_iter;
my $script = $buffer->get_text( $begin_iter, $end_iter, 0 );
$self->_file_format->set_script($script);
$self->_generate_preview_file(0);
}
);