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 }