swift
artifact.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2017 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "misc/db/artifact.h"
5 
6 #include <QRegularExpression>
7 #include <QStringBuilder>
8 
9 #include "config/buildconfig.h"
10 #include "misc/stringutils.h"
11 
12 using namespace swift::config;
13 using namespace swift::misc::network;
14 
15 SWIFT_DEFINE_VALUEOBJECT_MIXINS(swift::misc::db, CArtifact)
16 
17 namespace swift::misc::db
18 {
19  CArtifact::CArtifact() {}
20 
21  CArtifact::CArtifact(const QString &name, const QString &version, const QString &md5, CArtifact::ArtifactType type,
22  int size, bool existing, const CPlatform &platform)
23  : m_name(name.trimmed()), m_md5(md5), m_type(static_cast<int>(type)), m_size(size), m_existing(existing),
24  m_platform(platform)
25  {
26  this->setVersion(trimVersionString(version));
27  if (!name.isEmpty() && version.isEmpty()) { m_version = versionNumberFromFilename(name); }
28  }
29 
30  bool CArtifact::matchesName(const QString &name, Qt::CaseSensitivity cs) const
31  {
32  const bool m =
33  (cs == Qt::CaseInsensitive) ? caseInsensitiveStringCompare(this->getName(), name) : name == this->getName();
34  if (m) { return true; }
35  return name.startsWith(this->getName(), cs);
36  }
37 
39  {
40  if (m_size < 0) { return {}; }
41  return CFileUtils::humanReadableFileSize(m_size);
42  }
43 
44  bool CArtifact::matchesAnyPlatform(const CPlatform &platform) const { return m_platform.matchesAny(platform); }
45 
46  bool CArtifact::hasUnrestrictedDistribution() const { return m_distributions.containsUnrestricted(); }
47 
48  bool CArtifact::isWithDistribution(const CDistribution &distribution, bool acceptMoreStableDistributions) const
49  {
50  if (distribution.isEmpty() || !this->hasDistributions()) { return false; }
51  for (const CDistribution &dist : this->getDistributions())
52  {
53  if (dist == distribution) { return true; }
54  if (acceptMoreStableDistributions && dist.isStabilityBetter(distribution)) { return true; }
55  }
56  return false;
57  }
58 
60  {
61  if (!this->hasDistributions()) { return CRemoteFile(); }
62  CRemoteFile rf(this->getName(), this->getFileSize());
63  const CDistribution d = this->getMostStableDistribution();
64  const CUrl url = d.getDownloadUrl();
65  if (url.isEmpty()) { return CRemoteFile(); }
66  rf.setUtcTimestamp(this->getUtcTimestamp());
67  rf.setUrl(url);
68  rf.setDescription(this->getPlatform().toQString() + " " + d.getChannel());
69  return rf;
70  }
71 
73  {
74  if (this->isUnknown()) { return false; }
75  if (!this->hasVersion()) { return false; }
76  return this->getQVersion() > CBuildConfig::getVersion();
77  }
78 
79  QString CArtifact::convertToQString(bool i18n) const { return this->convertToQString(", ", i18n); }
80 
81  QString CArtifact::convertToQString(const QString &separator, bool i18n) const
82  {
83  Q_UNUSED(i18n);
84  return u"name: " % this->getName() % separator % u"size: " % this->getFileSizeHumanReadable() % separator %
85  u"OS: " % this->getPlatform().toQString(i18n) % separator % u"timestamp: " %
87  }
88 
90  {
91  if (index.isMyself()) { return QVariant::fromValue(*this); }
93  {
95  }
96 
97  const ColumnIndex i = index.frontCasted<ColumnIndex>();
98  switch (i)
99  {
100  case IndexName: return QVariant::fromValue(m_name);
101  case IndexMd5: return QVariant::fromValue(m_md5);
102  case IndexPlatform: return m_platform.propertyByIndex(index.copyFrontRemoved());
103  case IndexType: return QVariant::fromValue(m_type);
104  case IndexSize: return QVariant::fromValue(m_size);
105  case IndexSizeHumanReadable: return QVariant::fromValue(this->getFileSizeHumanReadable());
106  case IndexVersionString: return QVariant::fromValue(m_version);
107  case IndexQVersion: return QVariant::fromValue(this->getQVersion());
108  case IndexDistributions: return QVariant::fromValue(m_distributions);
109  default: return CValueObject::propertyByIndex(index);
110  }
111  }
112 
113  void CArtifact::setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
114  {
115  if (index.isMyself())
116  {
117  (*this) = variant.value<CArtifact>();
118  return;
119  }
121  {
123  return;
124  }
125 
126  const ColumnIndex i = index.frontCasted<ColumnIndex>();
127  switch (i)
128  {
129  case IndexName: this->setName(variant.toString()); break;
130  case IndexMd5: m_md5 = variant.toString(); break;
131  case IndexPlatform: m_platform.setPropertyByIndex(index.copyFrontRemoved(), variant); break;
132  case IndexType: m_type = variant.toInt(); break;
133  case IndexSize: m_size = variant.toInt(); break;
134  case IndexVersionString: m_version = variant.toString(); break;
135  case IndexDistributions: m_distributions = variant.value<CDistributionList>(); break;
136  default: CValueObject::setPropertyByIndex(index, variant); break;
137  }
138  }
139 
140  CArtifact CArtifact::fromDatabaseJson(const QJsonObject &json, const QString &prefix)
141  {
142  Q_UNUSED(prefix); // not nested
143 
144  const QString name = json.value("name").toString();
145  const QString md5 = json.value("md5").toString();
146  const QString version = json.value("version").toString();
147  const CPlatform platform = CPlatform(json.value("os").toString());
148  const CArtifact::ArtifactType type = stringToType(json.value("type").toString());
149  const int size = json.value("size").toInt(-1);
150  const bool existing = json.value("existing").toBool();
151 
152  CArtifact artifact(name, version, md5, type, size, existing, platform);
154  if (json.contains("distributions"))
155  {
156  const QJsonObject distJson = json.value("distributions").toObject();
157  if (!distJson.isEmpty() && distJson.contains("distributionArray"))
158  {
159  const CDistributionList distributions =
160  CDistributionList::fromDatabaseJson(distJson.value("distributionArray").toArray());
161  artifact.setDistributions(distributions);
162  }
163  }
164  return artifact;
165  }
166 
168  {
169  static const QString xswb("xswiftbus");
170  static const QString installer("pilot client installer");
171  static const QString symbols("symbols");
172  static const QString unknown("unknown");
173 
174  switch (type)
175  {
176  case XSwiftBus: return xswb;
177  case PilotClientInstaller: return installer;
178  case Symbols: return symbols;
179  case UnknownArtifact:
180  default: break;
181  }
182  return unknown;
183  }
184 
186  {
187  if (name.isEmpty()) { return CPlatform::unknownOs(); }
188  const QString n(name.toLower().trimmed());
189  if (n.contains("-windows-") || n.endsWith(".exe"))
190  {
191  if (n.contains("-64-")) { return CPlatform::win64Platform(); }
192  if (n.contains("-32-")) { return CPlatform::win32Platform(); }
193  return CPlatform::unknownOs();
194  }
195 
196  if (n.contains("-macos-") || n.endsWith(".dmg")) { return CPlatform::macOSPlatform(); }
197  if (n.contains("-linux-") || n.endsWith(".run")) { return CPlatform::linuxPlatform(); }
198  if (n.contains("-allos-")) { return CPlatform::allOs(); }
199 
200  return CPlatform::unknownOs();
201  }
202 
203  QString CArtifact::versionNumberFromFilename(const QString &filename)
204  {
205  if (filename.isEmpty()) { return {}; }
206 
207  // swiftinstaller-linux-64-0.9.2.123.run
208  thread_local const QRegularExpression regex { R"(\d+\.\d+\.\d+\.\d+)" };
209  const QRegularExpressionMatch match = regex.match(filename);
210  return match.captured();
211  }
212 
213  CArtifact::ArtifactType CArtifact::stringToType(const QString &str)
214  {
215  const QString s(str.trimmed().toLower());
216  if (s.contains("installer")) return CArtifact::PilotClientInstaller;
217  if (s.contains("client")) return CArtifact::PilotClientInstaller;
218  if (s.contains("symb")) return CArtifact::Symbols;
219  if (s.contains("bus")) return CArtifact::XSwiftBus;
220  return CArtifact::UnknownArtifact;
221  }
222 
223  QString CArtifact::trimVersionString(const QString &version)
224  {
225  if (version.count('.') != 3) return version; // no 4th segment
226  QStringList parts = version.split('.');
227  const QString p4 = trim4thSegment(parts[3]);
228  if (p4 == parts[3]) { return version; } // nothing changed
229  parts[3] = p4;
230  const QString v = parts.join('.');
231  return v;
232  }
233 
234  QString CArtifact::trim4thSegment(const QString &seg)
235  {
236  // old schema: yMMddHHmm (9)
237  if (seg.length() >= 9) { return QStringLiteral("0"); }
238  return seg;
239  }
240 } // namespace swift::misc::db
static QString humanReadableFileSize(qint64 size)
Human readable (GB, MB, ..) file size.
Definition: fileutils.cpp:516
Platform (i.e.
Definition: platform.h:24
bool matchesAny(const CPlatform &otherPlatform) const
Matches any other platform.
Definition: platform.cpp:29
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: platform.cpp:82
static const CPlatform & linuxPlatform()
Linux.
Definition: platform.cpp:193
static const CPlatform & macOSPlatform()
Mac OS.
Definition: platform.cpp:199
static const CPlatform & unknownOs()
Unknown OS.
Definition: platform.cpp:205
static const CPlatform & win32Platform()
Win32.
Definition: platform.cpp:181
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: platform.cpp:71
static const CPlatform & win64Platform()
Win64.
Definition: platform.cpp:187
static const CPlatform & allOs()
All OS.
Definition: platform.cpp:211
Non-owning reference to a CPropertyIndex with a subset of its features.
Q_REQUIRED_RESULT CPropertyIndexRef copyFrontRemoved() const
Copy with first element removed.
CastType frontCasted() const
First element casted to given type, usually the PropertIndex enum.
bool isMyself() const
Myself index, used with nesting.
QString getFormattedUtcTimestampYmdhms() const
As yyyy MM dd HH mm ss.
QDateTime getUtcTimestamp() const
Get timestamp.
void setUtcTimestamp(const QDateTime &timestamp)
Set timestamp.
Artifacts ("our software" products)
Definition: artifact.h:23
ColumnIndex
Properties by index.
Definition: artifact.h:27
bool hasUnrestrictedDistribution() const
Has unrestricted distribution.
Definition: artifact.cpp:46
bool isWithDistribution(const CDistribution &distribution, bool acceptMoreStableDistributions) const
Is distributed with given distribution?
Definition: artifact.cpp:48
bool hasDistributions() const
Has distributions?
Definition: artifact.h:107
void setName(const QString &name)
Set the name.
Definition: artifact.h:62
bool matchesAnyPlatform(const CPlatform &platform) const
Matches any platform.
Definition: artifact.cpp:44
void setPropertyByIndex(swift::misc::CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: artifact.cpp:113
QString getFileSizeHumanReadable() const
Human readable (GB, MB, ..) file size.
Definition: artifact.cpp:38
network::CRemoteFile asRemoteFile() const
Turn into remote file.
Definition: artifact.cpp:59
bool isUnknown() const
Unknown.
Definition: artifact.h:71
int getFileSize() const
File size.
Definition: artifact.h:77
const CPlatform & getPlatform() const
OS.
Definition: artifact.h:89
QVariant propertyByIndex(swift::misc::CPropertyIndexRef index) const
Property by index.
Definition: artifact.cpp:89
static CPlatform artifactNameToPlatform(const QString &name)
Name to platform.
Definition: artifact.cpp:185
bool isNewerThanCurrentBuild() const
Newer than the current build.
Definition: artifact.cpp:72
void setDistributions(const CDistributionList &distributions)
Related distributions.
Definition: artifact.h:104
static const QString & typeToString(ArtifactType type)
Type as string.
Definition: artifact.cpp:167
static CArtifact fromDatabaseJson(const QJsonObject &json, const QString &prefix={})
Object from database JSON format.
Definition: artifact.cpp:140
bool matchesName(const QString &name, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Matching name?
Definition: artifact.cpp:30
CDistribution getMostStableDistribution() const
Most stable distribution if any.
Definition: artifact.h:101
const CDistributionList & getDistributions() const
Related distributions.
Definition: artifact.h:98
QString convertToQString(bool i18n=false) const
Cast as QString.
Definition: artifact.cpp:79
const QString & getName() const
Name (i.e. installer name, symbol name)
Definition: artifact.h:59
Distributions for channel.
Definition: distribution.h:27
const QString & getChannel() const
Version channel (Alpha, Beta, Stable ..)
Definition: distribution.h:45
bool isEmpty() const
Empty?
Definition: distribution.h:81
const network::CUrl & getDownloadUrl() const
Download URL, i.e. here one can download installer.
Definition: distribution.h:57
Multiple distributions for different channels:
bool containsUnrestricted() const
Contains any unrestricted.
static CDistributionList fromDatabaseJson(const QJsonArray &array)
From database JSON by array.
QVersionNumber getQVersion() const
Version as QVersion.
Definition: datastore.cpp:16
QString m_version
version info
Definition: datastore.h:63
bool hasVersion() const
Having a version?s.
Definition: datastore.h:54
void setVersion(const QString &version)
Version info.
Definition: datastore.h:57
QVariant propertyByIndex(swift::misc::CPropertyIndexRef index) const
Property by index.
Definition: datastore.cpp:94
static bool canHandleIndex(swift::misc::CPropertyIndexRef index)
Can given index be handled?
Definition: datastore.cpp:147
void setKeyVersionTimestampFromDatabaseJson(const QJsonObject &json, const QString &prefix=QString())
Set key and timestamp values.
Definition: datastore.cpp:79
void setPropertyByIndex(swift::misc::CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: datastore.cpp:110
void setPropertyByIndex(CPropertyIndexRef index, const QVariant &variant)
Set property by index.
Definition: mixinindex.h:160
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: mixinindex.h:167
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
void setDescription(const QString &description)
Description.
Definition: remotefile.h:73
void setUrl(const CUrl &url)
Set URL.
Definition: remotefile.h:94
Value object encapsulating information of a location, kind of simplified CValueObject compliant versi...
Definition: url.h:27
bool isEmpty() const
Empty.
Definition: url.cpp:54
Plugin loaded by X-Plane which publishes a DBus service.
Definition: command.h:14
SWIFT_MISC_EXPORT bool caseInsensitiveStringCompare(const QString &c1, const QString &c2)
Case insensitive string compare.
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.
Definition: valueobject.h:67