#include "denote_metadata.h" #include #include namespace Denote { QDateTime Metadata::creationDate() const { if (identifier.isEmpty()) return {}; return QDateTime::fromString(identifier, QStringLiteral("yyyyMMdd'T'HHmmss")); } Metadata parseFilename(const QString &path) { Metadata meta; meta.path = path; const QString fname = QFileInfo(path).fileName(); // Identifier: leading YYYYMMDDTHHMMSS static const QRegularExpression reId(QStringLiteral("^(\\d{8}T\\d{6})")); auto m = reId.match(fname); if (m.hasMatch()) meta.identifier = m.captured(1); // Signature: ==sig-- (between identifier and title) static const QRegularExpression reSig(QStringLiteral("==([^-]+?)--")); m = reSig.match(fname); if (m.hasMatch()) meta.signature = m.captured(1); // Title: --title (first -- delimited segment, before __ or .) static const QRegularExpression reTitle(QStringLiteral("--([^_\\.]+)")); m = reTitle.match(fname); if (m.hasMatch()) { QString raw = m.captured(1); raw.replace(QLatin1Char('-'), QLatin1Char(' ')); meta.title = raw; } // Tags: __tag1_tag2 (before extension or end) static const QRegularExpression reTags(QStringLiteral("__(.+?)(?:\\.|$)")); m = reTags.match(fname); if (m.hasMatch()) { meta.tags = m.captured(1).split(QLatin1Char('_'), Qt::SkipEmptyParts); } return meta; } QString generateIdentifier() { return QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd'T'HHmmss")); } QString slugifyTitle(const QString &title) { QString slug = title.toLower(); slug.replace(QLatin1Char(' '), QLatin1Char('-')); slug.replace(QLatin1Char('_'), QLatin1Char('-')); // Remove anything that isn't a-z, 0-9, or hyphen static const QRegularExpression reClean(QStringLiteral("[^a-z0-9-]")); slug.remove(reClean); return slug; } bool isValidTag(const QString &tag) { if (tag.isEmpty()) return false; for (const QChar &ch : tag) { if (!ch.isLetterOrNumber()) return false; if (ch.isLetter() && ch.isUpper()) return false; } return true; } QString buildFilename(const QString &title, const QStringList &tags, const QString &signature, const QString &ext) { QString result = generateIdentifier(); // Signature if (!signature.isEmpty()) { QString sig = signature.toLower(); sig.replace(QLatin1Char(' '), QStringLiteral("==")); sig.replace(QLatin1Char('_'), QStringLiteral("==")); // Remove special chars static const QRegularExpression reSigClean( QStringLiteral("[{}!@#$%^&*()+\'\"?,.\\\\|;:~`\u2018\u2019\u201c\u201d/-]")); sig.remove(reSigClean); // Normalize consecutive equals static const QRegularExpression reEq(QStringLiteral("={3,}")); sig.replace(reEq, QStringLiteral("==")); // Trim trailing = while (sig.endsWith(QLatin1Char('='))) sig.chop(1); result += QStringLiteral("==") + sig; } // Title result += QStringLiteral("--") + slugifyTitle(title); // Tags if (!tags.isEmpty()) { result += QStringLiteral("__") + tags.join(QLatin1Char('_')); } // Extension result += ext; return result; } } // namespace Denote