Держи. Постановка готова: --trash, --yes, --json, README
Постановка. Цель: безопасная очистка. dupes --trash <ПАПКА> показывает, какие лишние копии уйдут в корзину. dupes --trash --yes <ПАПКА> действительно переносит их в корзину системы. --json выдаёт отчёт для скриптов. Плюс README и версия 0.1.0.
Что проверено в окружении. Крейт trash 5.2.9 (MIT, подходит под Rust 1.97 и edition 2024) добавляет шесть небольших крейтов, работает без рабочего стола и D-Bus. Корзина ищется в XDG_DATA_HOME, а если он пуст — в домашнем каталоге; при подмене переменных для дочернего процесса файл попадает во временную корзину, настоящая не тронута. Файл на другой файловой системе уходит в служебную папку корзины в корне тома; в тестах проверяемая папка и подменённая корзина должны лежать в одном временном каталоге. Ошибки крейта: файла нет — NotFound; XDG_DATA_HOME указывает на обычный файл — NotADirectory, исходный файл остаётся на месте, это удобный способ проверить «корзина недоступна» даже под root; нет ни HOME, ни XDG_DATA_HOME — Unknown. Тесты идут от root, ошибки прав воспроизвести нельзя. Новые зависимости: trash, serde, serde_json; чистая сборка станет дольше на десятки секунд, до лимита далеко.
План. 1) Версия 0.1.0 и зависимости. 2) В обходе у файла появляется время изменения, и функция повторной проверки перед переносом: изменённым считается файл с другим размером, другим временем или переставший быть обычным файлом. 3) Группа дубликатов хранит оставляемый файл и копии; оставляется самый ранний по времени изменения, затем с более коротким путём, затем по алфавиту. 4) В похожих фото файл с временем доходит до перепроверки; оставляется фото лучшего качества, как в задаче 2. 5) У причин пропуска появляется стабильный код. 6) Новый модуль очистки, не знающий о крейте корзины: он перепроверяет оставляемый файл (если изменился, группа не тронута и перенос не вызывается), затем каждый лишний файл, ошибка на одном не останавливает остальные; сам перенос передаётся функцией-параметром, потому что реализаций две — корзина системы и подменная в модульных тестах. 7) Отдельный модуль единственным местом использует крейт trash и переводит его ошибки: нет файла, нет прав, остальное — непредвиденная ошибка. 8) В разборе аргументов флаги --trash (пробный запуск), --yes (только вместе с --trash), --json и -V/--version, русская справка, подсказка при --yes без --trash, нейтральное сообщение о повторе параметра «укажите его один раз». 9) Отчёт показывает три действия: просто отчёт с подсказкой про --trash; пробный запуск со строками «остаётся» и «в корзину» и пометкой «Это пробный запуск: файлы остались на месте»; перенос с результатами по файлам, итогом и честной фразой, что место освободится после очистки корзины. 10) Модуль JSON-отчёта со стабильными английскими ключами, которые всегда присутствуют (неприменимые равны null): режим, порог, действие, группы с оставляемым и лишними, освобождаемые байты, число и размер перенесённых и неудачных, пропущенные файлы с кодом и русским сообщением. 11) Главная функция: перенос только в режиме --yes, в режиме --json пропуски только в поле skipped, stderr пуст, новый код возврата 3 при неудаче хотя бы одного переноса. 12) README по-русски для неспециалистов: что делает, установка, примеры (дубликаты, похожие фото и порог, пробный запуск, перенос, JSON, версия), что остаётся в группе, коды возврата, ничего не удаляется навсегда. 13) В правила проекта добавляется правило: тесты не трогают настоящую корзину, перенос проверяется только дочерним процессом с подменёнными XDG_DATA_HOME и HOME внутри того же временного каталога, под Linux; модульные тесты передают свою функцию переноса. Коммиты по Conventional Commits, каждый с зелёным make check.
Критерии готовности: make check зелёный; обычные запуски файлы не трогают, оставляемый файл идёт первым с пометкой; --trash не меняет ни одного файла и не создаёт корзину, код 0; --trash --yes оставляет самый ранний файл, остальные оказываются во временной корзине с записью об исходном пути, код 0; при равном времени остаётся файл с более коротким путём, затем по алфавиту; --similar --trash --yes оставляет лучшее фото; недоступная корзина — файлы на месте, по каждому причина, итог, код 3; изменённый или пропавший после сканирования файл не переносится, остальные переносятся, а при изменении оставляемого файла группа не тронута; --yes без --trash даёт код 2 с подсказкой; повтор флагов даёт код 2 с нейтральным сообщением; --json во всех сочетаниях даёт разбираемый JSON, stderr пуст, ошибка папки при --json — пустой stdout, сообщение в stderr и код 1; dupes --version и -V выводят «dupes 0.1.0»; README содержит все примеры; ни один тест не обращается к настоящей корзине.
Тесты модульные (обход, выбор оставляемого, очистка с подменной функцией переноса, перевод ошибок корзины, коды причин, тексты отчёта, JSON, разбор аргументов) и интеграционные под Linux с подменой XDG_DATA_HOME и HOME в одном временном каталоге: пробный запуск, перенос с самым старым, ничья по времени, похожие фото, недоступная корзина, --json, --version, справка.
Вне задачи: восстановление из корзины и её очистка; интерактивное подтверждение; ход выполнения; повторное хеширование перед переносом (проверяются размер и время, окно между проверкой и переносом остаётся); жёсткие ссылки; исключение самой корзины при проверке домашней папки целиком. Замечено по пути, не чиним: в правилах проекта в разделе «Команды» ещё написано «пока не заданы», хотя make check уже запускается.
Принятые решения. Без --trash только отчёт; --trash — пробный запуск; --trash --yes — перенос; --yes без --trash — ошибка; в тексте «пробный запуск» вместо «сухой прогон», так понятнее. Оставляется самый ранний по времени изменения, потому что время создания есть не везде. --trash работает с --similar и оставляет фото лучшего качества, иначе могла бы остаться уменьшенная копия. Перед переносом проверяются файл и оставляемый файл; сбой одного переноса не останавливает остальные, итоговый код 3; ничего не удаляется навсегда. Сбои показываются у самого файла в stdout и в итоге. В JSON ключи английские и стабильные, причины даются кодом и русским сообщением, имена не в UTF-8 выводятся с заменой. Подмена переноса — параметр-функция без трейта и фабрики. Честно сказано, что место освободится после очистки корзины. Добавлены --version и -V. Вопросов нет.