From 10b09c35756928dded2284984ff156c0f557b27d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 23 Mar 2017 23:46:25 +0100 Subject: [PATCH] WIP: create packages from templates Now, packages are actually created. --- src/lay/laySalt.cc | 124 +++++++++++++++++++++++++++++--- src/lay/laySalt.h | 2 +- src/lay/laySaltGrain.cc | 2 +- src/lay/laySaltManagerDialog.cc | 94 ++++++++++++++---------- src/unit_tests/laySalt.cc | 8 ++- 5 files changed, 182 insertions(+), 48 deletions(-) diff --git a/src/lay/laySalt.cc b/src/lay/laySalt.cc index 6e3ba10cb..b71cbc107 100644 --- a/src/lay/laySalt.cc +++ b/src/lay/laySalt.cc @@ -29,6 +29,7 @@ #include #include +#include namespace lay { @@ -215,8 +216,91 @@ Salt::remove_grain (const SaltGrain &grain) } } +namespace +{ + +/** + * @brief A helper class required because directory traversal is not supported by QResource directly + * This class supports resource file trees and extraction of a tree from the latter + */ +class ResourceDir + : public QResource +{ +public: + using QResource::isFile; + + /** + * @brief Constructor + * Creates a resource representing a resource tree. + */ + ResourceDir (const QString &path) + : QResource (path) + { + // .. nothing yet .. + } + + /** + * @brief Writes the resource tree to the target directory + * Returns false on error - see log in this case. + */ + bool copy_to (const QDir &target) + { + if (isDir ()) { + + QStringList templ_dir = children (); + for (QStringList::const_iterator t = templ_dir.begin (); t != templ_dir.end (); ++t) { + + ResourceDir child_res (fileName () + QString::fromUtf8 ("/") + *t); + if (child_res.isFile ()) { + + QFile file (target.absoluteFilePath (*t)); + if (! file.open (QIODevice::WriteOnly)) { + tl::error << QObject::tr ("Unable to open target file for writing: %1").arg (target.absoluteFilePath (*t)); + return false; + } + + QByteArray data; + if (child_res.isCompressed ()) { + data = qUncompress ((const unsigned char *)child_res.data (), (int)child_res.size ()); + } else { + data = QByteArray ((const char *)child_res.data (), (int)child_res.size ()); + } + + file.write (data); + + file.close (); + + } else { + + QFileInfo child_dir (target.absoluteFilePath (*t)); + if (! child_dir.exists ()) { + if (! target.mkdir (*t)) { + tl::error << QObject::tr ("Unable to create target directory: %1").arg (child_dir.path ()); + return false; + } + } else if (! child_dir.isDir ()) { + tl::error << QObject::tr ("Unable to create target directory (is a file already): %1").arg (child_dir.path ()); + return false; + } + + if (! child_res.copy_to (QDir (target.absoluteFilePath (*t)))) { + return false; + } + + } + + } + + } + + return true; + } +}; + +} + bool -Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager &download_manager) +Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager *download_manager) { tl_assert (!m_root.is_empty ()); @@ -243,15 +327,23 @@ Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManag // change down to the desired target location and create the directory structure while doing so std::vector name_parts = tl::split (target.name (), "/"); for (std::vector::const_iterator n = name_parts.begin (); n != name_parts.end (); ++n) { - QDir subdir (target_dir.filePath (tl::to_qstring (*n))); - if (! subdir.exists ()) { + + QFileInfo subdir (target_dir.filePath (tl::to_qstring (*n))); + if (subdir.exists () && ! subdir.isDir ()) { + throw tl::Exception (tl::to_string (tr ("Unable to create target directory '%1' for installing package - is already a file").arg (subdir.path ()))); + } else if (! subdir.exists ()) { if (! target_dir.mkdir (tl::to_qstring (*n))) { throw tl::Exception (tl::to_string (tr ("Unable to create target directory '%1' for installing package").arg (subdir.path ()))); } if (! target_dir.cd (tl::to_qstring (*n))) { throw tl::Exception (tl::to_string (tr ("Unable to change to target directory '%1' for installing package").arg (subdir.path ()))); } + } else { + if (! target_dir.cd (tl::to_qstring (*n))) { + throw tl::Exception (tl::to_string (tr ("Unable to change to target directory '%1' for installing package").arg (subdir.path ()))); + } } + } } catch (tl::Exception &ex) { @@ -261,30 +353,46 @@ Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManag bool res = true; + std::string target_name = target.name (); target = templ; target.set_path (tl::to_string (target_dir.absolutePath ())); + target.set_name (target_name); if (! templ.path ().empty ()) { - // if the template represents an actual folder, use the files from there - tl::info << QObject::tr ("Copying package from '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ())); - res = tl::cp_dir_recursive (templ.path (), target.path ()); + if (templ.path ()[0] != ':') { + + // if the template represents an actual folder, use the files from there + tl::info << QObject::tr ("Copying package from '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ())); + res = tl::cp_dir_recursive (templ.path (), target.path ()); + + } else { + + // if the template represents a resource path, use the files from there + tl::info << QObject::tr ("Installing package from resource '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ())); + res = ResourceDir (tl::to_qstring (templ.path ())).copy_to (QDir (tl::to_qstring (target.path ()))); + + } } else if (! templ.url ().empty ()) { + tl_assert (download_manager != 0); + // otherwise download from the URL tl::info << QObject::tr ("Downloading package from '%1' to '%2' ..").arg (tl::to_qstring (templ.url ())).arg (tl::to_qstring (target.path ())); - res = download_manager.download (templ.url (), target.path ()); + res = download_manager->download (templ.url (), target.path ()); } if (res) { tl::info << QObject::tr ("Package '%1' installed").arg (tl::to_qstring (target.name ())); - target.set_installed_time (QDateTime::currentDateTime ()); target.save (); + // NOTE: this is a bit brute force .. we could as well try to insert the new grain into the existing structure + refresh (); + } else { tl::warn << QObject::tr ("Failed to install package '%1' - removing files ..").arg (tl::to_qstring (target.name ())); diff --git a/src/lay/laySalt.h b/src/lay/laySalt.h index 4ade8acf9..ad578c737 100644 --- a/src/lay/laySalt.h +++ b/src/lay/laySalt.h @@ -152,7 +152,7 @@ public: * * Returns true, if the package could be created successfully. */ - bool create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager &download_manager); + bool create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager *download_manager = 0); signals: /** diff --git a/src/lay/laySaltGrain.cc b/src/lay/laySaltGrain.cc index af3f39130..246d44278 100644 --- a/src/lay/laySaltGrain.cc +++ b/src/lay/laySaltGrain.cc @@ -226,7 +226,7 @@ SaltGrain::valid_name (const std::string &n) // this captures the cases where the extractor skips blanks // TODO: the extractor should have a "non-blank-skipping" mode - return s == n; + return res == n; } bool diff --git a/src/lay/laySaltManagerDialog.cc b/src/lay/laySaltManagerDialog.cc index 71a34dba3..568040404 100644 --- a/src/lay/laySaltManagerDialog.cc +++ b/src/lay/laySaltManagerDialog.cc @@ -25,6 +25,7 @@ #include "laySalt.h" #include "ui_SaltGrainTemplateSelectionDialog.h" #include "tlString.h" +#include "tlExceptions.h" #include #include @@ -152,7 +153,7 @@ public: void update () { - // @@@ + reset (); } public: @@ -230,8 +231,8 @@ class SaltGrainTemplateSelectionDialog : public QDialog, private Ui::SaltGrainTemplateSelectionDialog { public: - SaltGrainTemplateSelectionDialog (QWidget *parent) - : QDialog (parent) + SaltGrainTemplateSelectionDialog (QWidget *parent, lay::Salt *salt) + : QDialog (parent), mp_salt (salt) { Ui::SaltGrainTemplateSelectionDialog::setupUi (this); @@ -249,11 +250,14 @@ public: SaltGrain *g = model->grain_from_index (salt_view->currentIndex ()); tl_assert (g != 0); - g->set_name (tl::to_string (name_edit->text ().simplified ())); - return *g; } + std::string name () const + { + return tl::to_string (name_edit->text ()); + } + void accept () { name_alert->clear (); @@ -263,12 +267,23 @@ public: } else if (! SaltGrain::valid_name (name)) { name_alert->error () << tr ("Name is not valid (must be composed of letters, digits or underscores.\nGroups and names need to be separated with slashes."); } else { + + // check, if this name does not exist yet + for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) { + if ((*g)->name () == name) { + name_alert->error () << tr ("A package with this name already exists"); + return; + } + } + QDialog::accept (); + } } private: lay::Salt m_salt_templates; + lay::Salt *mp_salt; }; // -------------------------------------------------------------------------------------- @@ -310,11 +325,6 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent) connect (mp_salt, SIGNAL (collections_changed ()), this, SLOT (salt_changed ())); - // select the first grain - if (model->rowCount (QModelIndex ()) > 0) { - salt_view->setCurrentIndex (model->index (0, 0, QModelIndex ())); - } - salt_changed (); connect (salt_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_changed ())); @@ -334,40 +344,41 @@ SaltManagerDialog::edit_properties () } } -// @@@ -namespace -{ - -/** - * @brief A helper class required because directory traversal is not supported by QResource directly - */ -class OpenResource - : public QResource -{ -public: - using QResource::isDir; - using QResource::isFile; - using QResource::children; - - OpenResource (const QString &path) - : QResource (path) - { - // .. nothing yet .. - } -}; - -} -// @@@ - void SaltManagerDialog::create_grain () { - SaltGrainTemplateSelectionDialog temp_dialog (this); +BEGIN_PROTECTED + + SaltGrainTemplateSelectionDialog temp_dialog (this, mp_salt); if (temp_dialog.exec ()) { - // @@@ + SaltGrain target; + target.set_name (temp_dialog.name ()); + + if (mp_salt->create_grain (temp_dialog.templ (), target)) { + + // select the new one + SaltModel *model = dynamic_cast (salt_view->model ()); + if (model) { + for (int i = model->rowCount (QModelIndex ()); i > 0; ) { + --i; + QModelIndex index = model->index (i, 0, QModelIndex ()); + SaltGrain *g = model->grain_from_index (index); + if (g && g->name () == target.name ()) { + salt_view->setCurrentIndex (index); + break; + } + } + + } + + } else { + throw tl::Exception (tl::to_string (tr ("Initialization of new package failed - see log window (File/Log Viewer) for details"))); + } } + +END_PROTECTED } void @@ -399,11 +410,20 @@ SaltManagerDialog::salt_changed () m_current_changed_enabled = true; if (mp_salt->is_empty ()) { + list_stack->setCurrentIndex (1); details_frame->hide (); + } else { + list_stack->setCurrentIndex (0); details_frame->show (); + + // select the first grain + if (model->rowCount (QModelIndex ()) > 0) { + salt_view->setCurrentIndex (model->index (0, 0, QModelIndex ())); + } + } current_changed (); diff --git a/src/unit_tests/laySalt.cc b/src/unit_tests/laySalt.cc index 62a62826a..9d3ef0a36 100644 --- a/src/unit_tests/laySalt.cc +++ b/src/unit_tests/laySalt.cc @@ -162,6 +162,13 @@ TEST (2) EXPECT_EQ (lay::SaltGrain::valid_version ("\t1 . 2.\n3"), true); EXPECT_EQ (lay::SaltGrain::valid_version ("x"), false); EXPECT_EQ (lay::SaltGrain::valid_version ("1.2x"), false); + EXPECT_EQ (lay::SaltGrain::valid_name (""), false); + EXPECT_EQ (lay::SaltGrain::valid_name ("x"), true); + EXPECT_EQ (lay::SaltGrain::valid_name ("x1"), true); + EXPECT_EQ (lay::SaltGrain::valid_name ("x1 "), false); + EXPECT_EQ (lay::SaltGrain::valid_name ("x$1"), false); + EXPECT_EQ (lay::SaltGrain::valid_name ("x/y"), true); + EXPECT_EQ (lay::SaltGrain::valid_name ("x_y"), true); EXPECT_EQ (lay::SaltGrain::compare_versions ("", ""), 0); EXPECT_EQ (lay::SaltGrain::compare_versions ("1", "2"), -1); EXPECT_EQ (lay::SaltGrain::compare_versions ("1", ""), 1); @@ -185,7 +192,6 @@ TEST (2) EXPECT_EQ (lay::SaltGrain::compare_versions ("991", "990"), 1); } - TEST (3) { const QString grain_spec_file = QString::fromUtf8 ("grain.xml");