mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-05-30 02:49:12 -04:00
feat: Add settings page for providers (#353)
This commit is contained in:
165
sources/providersConfig/ProviderInstanceWriter.cpp
Normal file
165
sources/providersConfig/ProviderInstanceWriter.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ProviderInstanceWriter.hpp"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include <QSet>
|
||||
|
||||
#include "ProviderInstanceFactory.hpp"
|
||||
#include "TomlWriter.hpp"
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
namespace {
|
||||
|
||||
struct Tr
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(QtC::QodeAssist)
|
||||
};
|
||||
|
||||
constexpr int kIdentityKeyColumn = 11; // longest key: "description"
|
||||
constexpr int kLaunchKeyColumn = 15; // longest key: "ready_timeout_s"
|
||||
|
||||
void writeIdentityBlock(TomlSerializer::TomlWriter &w, const ProviderInstance &inst)
|
||||
{
|
||||
w.setKeyColumnWidth(0);
|
||||
w.writeInt(QStringLiteral("schema_version"), 1);
|
||||
w.writeBlankLine();
|
||||
|
||||
w.setKeyColumnWidth(kIdentityKeyColumn);
|
||||
w.writeString(QStringLiteral("name"), inst.name);
|
||||
w.writeString(QStringLiteral("client_api"), inst.clientApi);
|
||||
if (!inst.description.isEmpty())
|
||||
w.writeString(QStringLiteral("description"), inst.description);
|
||||
w.writeBlankLine();
|
||||
w.writeString(QStringLiteral("url"), inst.url);
|
||||
if (!inst.apiKeyRef.isEmpty())
|
||||
w.writeString(QStringLiteral("api_key_ref"), inst.apiKeyRef);
|
||||
}
|
||||
|
||||
void writeExtrasBlock(TomlSerializer::TomlWriter &w, const QJsonObject &extras)
|
||||
{
|
||||
w.writeBlankLine();
|
||||
w.writeTableHeader(QStringLiteral("extras"));
|
||||
w.setKeyColumnWidth(0);
|
||||
w.writeJsonPrimitives(extras);
|
||||
}
|
||||
|
||||
void writeLaunchBlock(TomlSerializer::TomlWriter &w, const LaunchConfig &l)
|
||||
{
|
||||
w.writeBlankLine();
|
||||
w.writeTableHeader(QStringLiteral("launch"));
|
||||
w.setKeyColumnWidth(kLaunchKeyColumn);
|
||||
w.writeString(QStringLiteral("command"), l.command);
|
||||
if (!l.args.isEmpty())
|
||||
w.writeStringArray(QStringLiteral("args"), l.args);
|
||||
if (!l.cwd.isEmpty())
|
||||
w.writeString(QStringLiteral("cwd"), l.cwd);
|
||||
if (!l.readyUrl.isEmpty())
|
||||
w.writeString(QStringLiteral("ready_url"), l.readyUrl);
|
||||
w.writeInt(QStringLiteral("ready_timeout_s"), l.readyTimeout.count());
|
||||
w.writeBool(QStringLiteral("auto_start"), l.autoStart);
|
||||
w.writeBool(QStringLiteral("detach"), l.detach);
|
||||
|
||||
if (!l.env.isEmpty()) {
|
||||
w.writeBlankLine();
|
||||
w.writeTableHeader(QStringLiteral("launch.env"));
|
||||
w.setKeyColumnWidth(0);
|
||||
w.writeStringDict(l.env);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString ProviderInstanceWriter::toToml(const ProviderInstance &inst)
|
||||
{
|
||||
TomlSerializer::TomlWriter w;
|
||||
writeIdentityBlock(w, inst);
|
||||
if (!inst.extras.isEmpty())
|
||||
writeExtrasBlock(w, inst.extras);
|
||||
if (!inst.launch.isEmpty())
|
||||
writeLaunchBlock(w, inst.launch);
|
||||
return w.result();
|
||||
}
|
||||
|
||||
QString ProviderInstanceWriter::deriveBaseName(const QString &name)
|
||||
{
|
||||
QString baseName;
|
||||
for (QChar c : name) {
|
||||
if (c.isLetterOrNumber())
|
||||
baseName.append(c.toLower());
|
||||
else if (c == QLatin1Char(' ') || c == QLatin1Char('-') || c == QLatin1Char('_'))
|
||||
baseName.append(QLatin1Char('_'));
|
||||
}
|
||||
while (baseName.startsWith(QLatin1Char('_')))
|
||||
baseName.remove(0, 1);
|
||||
while (baseName.endsWith(QLatin1Char('_')))
|
||||
baseName.chop(1);
|
||||
if (baseName.isEmpty())
|
||||
baseName = QStringLiteral("instance");
|
||||
return baseName;
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr int kMaxCollisionRetries = 1000;
|
||||
} // namespace
|
||||
|
||||
QString ProviderInstanceWriter::pickUserFilePath(
|
||||
const QString &userDir, const QString &name, const QString &previousPath)
|
||||
{
|
||||
const QDir dir(userDir);
|
||||
const QString base = deriveBaseName(name);
|
||||
const QString preferred = dir.filePath(base + QLatin1String(".toml"));
|
||||
if (!previousPath.isEmpty()
|
||||
&& QFileInfo(previousPath).absolutePath() == dir.absolutePath()
|
||||
&& QFileInfo(previousPath).absoluteFilePath() == QFileInfo(preferred).absoluteFilePath())
|
||||
return preferred;
|
||||
QSet<QString> taken;
|
||||
for (const QString &existing : dir.entryList({"*.toml"}, QDir::Files))
|
||||
taken.insert(existing);
|
||||
if (!taken.contains(base + QLatin1String(".toml")))
|
||||
return preferred;
|
||||
for (int i = 2; i < kMaxCollisionRetries; ++i) {
|
||||
const QString candidate = QStringLiteral("%1_%2.toml").arg(base).arg(i);
|
||||
if (!taken.contains(candidate))
|
||||
return dir.filePath(candidate);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ProviderInstanceWriter::writeToUserDir(
|
||||
const ProviderInstance &inst, const QString &previousPath, QString *errorOut)
|
||||
{
|
||||
const QString userDir = ProviderInstanceFactory::userInstancesDir();
|
||||
if (!QDir().mkpath(userDir)) {
|
||||
if (errorOut)
|
||||
*errorOut = Tr::tr("Cannot create user provider folder:\n%1").arg(userDir);
|
||||
return {};
|
||||
}
|
||||
const QString filePath = pickUserFilePath(userDir, inst.name, previousPath);
|
||||
if (filePath.isEmpty()) {
|
||||
if (errorOut)
|
||||
*errorOut = Tr::tr("Cannot pick a free filename in:\n%1").arg(userDir);
|
||||
return {};
|
||||
}
|
||||
QSaveFile f(filePath);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
if (errorOut)
|
||||
*errorOut = Tr::tr("Cannot write %1:\n%2").arg(filePath, f.errorString());
|
||||
return {};
|
||||
}
|
||||
const QByteArray bytes = toToml(inst).toUtf8();
|
||||
if (f.write(bytes) != bytes.size() || !f.commit()) {
|
||||
if (errorOut)
|
||||
*errorOut = Tr::tr("Write failed for %1:\n%2").arg(filePath, f.errorString());
|
||||
return {};
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
Reference in New Issue
Block a user