Добавлен новый модуль rules для управления правилами отслеживания файлов в виде записей для gitignore

- create: создание правил из конфигурационного файла:
    - tracking - отслеживаемые файлы/пути
    - ignore - правила gitignore
- update: обновление существующих правил из конфигурационного файла
- reset: сброс изменений в правилах - откат на момент до внесения изменений в конфигурационный файл
- clear: очистить файл с правилами
- show: просмотр правил
- save: сохранить правила без возможности сброса (reset)
This commit is contained in:
Alexander Zhirov 2025-05-28 00:17:42 +03:00
parent db9a6be9f4
commit 5797e83f07
Signed by: alexander
GPG key ID: C8D8BE544A27C511
2 changed files with 165 additions and 0 deletions

View file

@ -2,3 +2,4 @@ module snag.core;
public import snag.core.core;
public import snag.core.exception;
public import snag.core.rules;

164
source/snag/core/rules.d Normal file
View file

@ -0,0 +1,164 @@
module snag.core.rules;
import snag.config;
import snag.core.exception;
import std.algorithm;
import std.array;
import std.path;
import std.stdio;
import std.string;
import std.conv;
import std.container;
import std.process;
import std.file;
class SnagRules {
private string[] _rules;
private SnagConfig _config;
private string[] _baseCommand;
private string _gitignore;
private string _gitignoreBak;
private auto git(string[] command, string message, string separator = ":\n\t") {
auto result = execute(_baseCommand ~ command);
if (result.status)
throw new SnagException(
message ~ separator ~ result.output.split('\n')[0]
);
return result;
}
private string[] generateGitignoreRules(string finalPath) {
string[] rules;
string[] parts = finalPath.split("/").filter!(p => !p.empty).array;
if (parts.length == 0) return rules;
rules ~= "/*";
rules ~= "!/" ~ parts[0];
rules ~= "/" ~ parts[0] ~ "/*";
if (parts.length > 1) {
string currentPath = "/" ~ parts[0];
foreach (i; 1 .. parts.length) {
currentPath ~= "/" ~ parts[i];
rules ~= "!" ~ currentPath;
if (i < parts.length.to!int - 1) {
rules ~= currentPath ~ "/*";
}
}
}
return rules;
}
private void generate() {
string[] rules;
auto tempRules = new RedBlackTree!string;
_config.tracking.each!(
track => rules ~= generateGitignoreRules(track)
);
rules.each!((rule) {
if (rule in tempRules) return;
tempRules.insert(rule);
_rules ~= rule;
});
_rules ~= _config.ignore;
}
this(SnagConfig config) {
_config = config;
_gitignore = config.git.buildPath("info/exclude");
_gitignoreBak = _gitignore ~ ".bak";
_baseCommand = format(
"git --git-dir=%s --work-tree=%s",
config.git, config.project
).split();
generate();
}
private void restoreUntracked() {
git(
["diff", "--cached", "--name-only"],
"Failed to retrieve the list of files removed from tracking"
).output.split('\n')[0..$-1].each!(
file => git(
["add", "-f", file],
"Failed to restore the file to tracking"
)
);
}
private void removeUntracked() {
git(
["ls-files", "-i", "-c", "--exclude-standard"],
"Failed to get the list of files to remove from tracking"
).output.split('\n')[0..$-1].each!(
file => git(
["rm", "--cached", file],
"Failed to remove file from tracking"
)
);
}
void create() {
auto file = File(_gitignore, "w");
_rules.each!(rule => file.writeln(rule));
file.close();
}
void update(bool remove) {
if (!_gitignoreBak.exists) {
readText(_gitignore).splitLines().equal(_rules) &&
throw new SnagException(
"Rule configuration is up-to-date and doesn't require updating"
);
copy(_gitignore, _gitignoreBak);
}
restoreUntracked();
create();
remove && removeUntracked();
}
void reset() {
!_gitignoreBak.exists &&
throw new SnagException(
"No rule changes to reset"
);
restoreUntracked();
rename(_gitignoreBak, _gitignore);
}
void clear() {
!readText(_gitignore).splitLines().length &&
throw new SnagException(
"The configuration has no rules"
);
!_gitignoreBak.exists && copy(_gitignore, _gitignoreBak);
restoreUntracked();
File(_gitignore, "w").close();
}
void show(bool config) {
if (config)
_rules.join('\n').writeln;
else
readText(_gitignore).write;
}
void save() {
if (!_gitignoreBak.exists) {
writeln("The rules are up to date");
return;
}
remove(_gitignoreBak);
writeln("The rules have been saved");
}
}