reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

index.cpp (4103B)


      1 /* ReaPack: Package manager for REAPER
      2  * Copyright (C) 2015-2025  Christian Fillion
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU Lesser General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU Lesser General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "index.hpp"
     19 
     20 #include "errors.hpp"
     21 #include "filesystem.hpp"
     22 #include "path.hpp"
     23 #include "remote.hpp"
     24 #include "xml.hpp"
     25 
     26 #include <cstring>
     27 #include <fstream>
     28 
     29 Path Index::pathFor(const std::string &name)
     30 {
     31   return Path::CACHE + (name + ".xml");
     32 }
     33 
     34 IndexPtr Index::load(const std::string &name, const char *data)
     35 {
     36   std::unique_ptr<std::istream> stream;
     37 
     38   if(data)
     39     stream = std::make_unique<std::istringstream>(data);
     40   else {
     41     stream = std::make_unique<std::ifstream>();
     42     if(!FS::open(*dynamic_cast<std::ifstream *>(stream.get()), pathFor(name)))
     43       throw reapack_error(FS::lastError());
     44   }
     45 
     46   XmlDocument doc(*stream);
     47 
     48   if(!doc)
     49     throw reapack_error(doc.error());
     50 
     51   const XmlNode &root = doc.root();
     52 
     53   if(!root || strcmp(root.name(), "index"))
     54     throw reapack_error("invalid index");
     55 
     56   int version = 0;
     57   root.attribute("version", &version);
     58 
     59   if(!version)
     60     throw reapack_error("index version not found");
     61 
     62   Index *ri = new Index(name);
     63 
     64   // ensure the memory is released if an exception is
     65   // thrown during the loading process
     66   std::unique_ptr<Index> ptr(ri);
     67 
     68   switch(version) {
     69   case 1:
     70     loadV1(root, ri);
     71     break;
     72   default:
     73     throw reapack_error("index version is unsupported");
     74   }
     75 
     76   ptr.release();
     77   return IndexPtr(ri);
     78 }
     79 
     80 Index::Index(const std::string &name)
     81   : m_name(name)
     82 {
     83 }
     84 
     85 Index::~Index()
     86 {
     87   for(const Category *cat : m_categories)
     88     delete cat;
     89 }
     90 
     91 void Index::setName(const std::string &newName)
     92 {
     93   if(!m_name.empty())
     94     throw reapack_error("index name is already set");
     95 
     96   // validation is taken care of later by Remote's constructor
     97   m_name = newName;
     98 }
     99 
    100 bool Index::addCategory(const Category *cat)
    101 {
    102   if(cat->index() != this)
    103     throw reapack_error("category belongs to another index");
    104 
    105   if(cat->packages().empty())
    106     return false;
    107 
    108   m_catMap.insert({cat->name(), m_categories.size()});
    109   m_categories.push_back(cat);
    110 
    111   m_packages.insert(m_packages.end(),
    112     cat->packages().begin(), cat->packages().end());
    113 
    114   return true;
    115 }
    116 
    117 const Category *Index::category(const std::string &name) const
    118 {
    119   const auto &it = m_catMap.find(name);
    120 
    121   if(it == m_catMap.end())
    122     return nullptr;
    123   else
    124     return category(it->second);
    125 }
    126 
    127 const Package *Index::find(const std::string &catName, const std::string &pkgName) const
    128 {
    129   if(const Category *cat = category(catName))
    130     return cat->package(pkgName);
    131   else
    132     return nullptr;
    133 }
    134 
    135 Category::Category(const std::string &name, const Index *ri)
    136   : m_index(ri), m_name(name)
    137 {
    138   if(m_name.empty())
    139     throw reapack_error("empty category name");
    140 }
    141 
    142 Category::~Category()
    143 {
    144   for(const Package *pack : m_packages)
    145     delete pack;
    146 }
    147 
    148 std::string Category::fullName() const
    149 {
    150   return m_index ? m_index->name() + "/" + m_name : m_name;
    151 }
    152 
    153 bool Category::addPackage(const Package *pkg)
    154 {
    155   if(pkg->category() != this)
    156     throw reapack_error("package belongs to another category");
    157 
    158   if(pkg->type() == Package::UnknownType || pkg->versions().empty())
    159     return false; // silently discard unknown package types
    160 
    161   m_pkgMap.insert({pkg->name(), m_packages.size()});
    162   m_packages.push_back(pkg);
    163   return true;
    164 }
    165 
    166 const Package *Category::package(const std::string &name) const
    167 {
    168   const auto &it = m_pkgMap.find(name);
    169 
    170   if(it == m_pkgMap.end())
    171     return nullptr;
    172   else
    173     return package(it->second);
    174 }