reapack

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

version.cpp (4762B)


      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 "version.hpp"
     19 
     20 #include "errors.hpp"
     21 #include "package.hpp"
     22 #include "source.hpp"
     23 
     24 #include <boost/lexical_cast.hpp>
     25 #include <cctype>
     26 #include <regex>
     27 
     28 std::string Version::displayAuthor(const std::string &author)
     29 {
     30   if(author.empty())
     31     return "Unknown";
     32   else
     33     return author;
     34 }
     35 
     36 Version::Version(const std::string &str, const Package *pkg)
     37   : m_name(str), m_time(), m_package(pkg)
     38 {
     39 }
     40 
     41 Version::~Version()
     42 {
     43   for(const Source *source : m_sources)
     44     delete source;
     45 }
     46 
     47 std::string Version::fullName() const
     48 {
     49   std::string name = m_package->fullName();
     50   name += " v";
     51   name += m_name.toString();
     52 
     53   return name;
     54 }
     55 
     56 bool Version::addSource(const Source *source)
     57 {
     58   if(source->version() != this)
     59     throw reapack_error("source belongs to another version");
     60   else if(!source->platform().test())
     61     return false;
     62 
     63   const Path path = source->targetPath();
     64 
     65   if(m_files.count(path))
     66     return false;
     67 
     68   m_files.insert(path);
     69   m_sources.push_back(source);
     70 
     71   return true;
     72 }
     73 
     74 std::ostream &operator<<(std::ostream &os, const Version &ver)
     75 {
     76   os << 'v' << ver.name().toString();
     77 
     78   if(!ver.author().empty())
     79     os << " by " << ver.author();
     80 
     81   if(ver.time())
     82     os << " – " << ver.time();
     83 
     84   os << "\r\n";
     85 
     86   const std::string &changelog = ver.changelog();
     87   os << String::indent(changelog.empty() ? "No changelog" : changelog);
     88 
     89   return os;
     90 }
     91 
     92 VersionName::VersionName() : m_stable(true)
     93 {}
     94 
     95 VersionName::VersionName(const std::string &str)
     96 {
     97   parse(str);
     98 }
     99 
    100 VersionName::VersionName(const VersionName &o)
    101   : m_string(o.m_string), m_segments(o.m_segments), m_stable(o.m_stable)
    102 {
    103 }
    104 
    105 void VersionName::parse(const std::string &str)
    106 {
    107   static const std::regex pattern("\\d+|[a-zA-Z]+");
    108 
    109   const auto &begin = std::sregex_iterator(str.begin(), str.end(), pattern);
    110   const std::sregex_iterator end;
    111 
    112   size_t letters = 0;
    113   std::vector<Segment> segments;
    114 
    115   for(std::sregex_iterator it = begin; it != end; it++) {
    116     const std::string &match = it->str(0);
    117 
    118     if(isalpha(match[0])) {
    119       if(segments.empty()) // got leading letters
    120         throw reapack_error(String::format("invalid version name '%s'", str.c_str()));
    121 
    122       segments.push_back(match);
    123       letters++;
    124     }
    125     else {
    126       try {
    127         segments.push_back(boost::lexical_cast<Numeric>(match));
    128       }
    129       catch(const boost::bad_lexical_cast &) {
    130         throw reapack_error(String::format("version segment overflow in '%s'", str.c_str()));
    131       }
    132     }
    133   }
    134 
    135   if(segments.empty()) // version doesn't have any numbers
    136     throw reapack_error(String::format("invalid version name '%s'", str.c_str()));
    137 
    138   m_string = str;
    139   swap(m_segments, segments);
    140   m_stable = letters < 1;
    141 }
    142 
    143 bool VersionName::tryParse(const std::string &str, std::string *errorOut)
    144 {
    145   try {
    146     parse(str);
    147     return true;
    148   }
    149   catch(const reapack_error &err) {
    150     if(errorOut)
    151       *errorOut = err.what();
    152 
    153     return false;
    154   }
    155 }
    156 
    157 auto VersionName::segment(const size_t index) const -> Segment
    158 {
    159   if(index < size())
    160     return m_segments[index];
    161   else
    162     return {};
    163 }
    164 
    165 int VersionName::compare(const VersionName &o) const
    166 {
    167   const size_t biggest = std::max(size(), o.size());
    168 
    169   switch(m_segments.empty() + o.m_segments.empty()) {
    170   case 1:
    171     return m_segments.empty() ? -1 : 1;
    172   case 2:
    173     return 0;
    174   }
    175 
    176   for(size_t i = 0; i < biggest; i++) {
    177     const Segment &lseg = segment(i);
    178     const Numeric *lnum = std::get_if<Numeric>(&lseg);
    179     const std::string *lstr = std::get_if<std::string>(&lseg);
    180 
    181     const Segment &rseg = o.segment(i);
    182     const Numeric *rnum = std::get_if<Numeric>(&rseg);
    183     const std::string *rstr = std::get_if<std::string>(&rseg);
    184 
    185     if(lnum && rnum) {
    186       if(*lnum < *rnum)
    187         return -1;
    188       else if(*lnum > *rnum)
    189         return 1;
    190     }
    191     else if(lstr && rstr) {
    192       if(*lstr < *rstr)
    193         return -1;
    194       else if(*lstr > *rstr)
    195         return 1;
    196     }
    197     else if(lnum && rstr)
    198       return 1;
    199     else if(lstr && rnum)
    200       return -1;
    201   }
    202 
    203   return 0;
    204 }