{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-23-14-00",
   "metadata": {},
   "source": [
    "# 23-14 · Безопасное имя при конфликте\n",
    "\n",
    "Практика к разделу [«Что делать, если имя уже занято»](../../site/chapters/glava-23/23-14-imya-zanyato.html). Настоящий файл — `projects/python/safesort/src/safesort/planner.py`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-01",
   "metadata": {},
   "source": [
    "## Цель\n",
    "\n",
    "Воспроизвести `_resolve_collision()` и убедиться, что свободное имя ищется по схеме `name (1).ext`, `name (2).ext`, ..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-02",
   "metadata": {},
   "source": [
    "## Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-14-03",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "\n",
    "def _resolve_collision(candidate, reserved):\n",
    "    if candidate not in reserved and not candidate.exists():\n",
    "        return candidate\n",
    "    stem, suffix, parent = candidate.stem, candidate.suffix, candidate.parent\n",
    "    counter = 1\n",
    "    while True:\n",
    "        alternative = parent / f\"{stem} ({counter}){suffix}\"\n",
    "        if alternative not in reserved and not alternative.exists():\n",
    "            return alternative\n",
    "        counter += 1\n",
    "\n",
    "\n",
    "papka = Path(\"/home/anna/Downloads/Sorted/documents\")\n",
    "kandidat = papka / \"otchet.pdf\"\n",
    "\n",
    "reserved = set()\n",
    "imena = []\n",
    "for _ in range(3):\n",
    "    novoe_imya = _resolve_collision(kandidat, reserved)\n",
    "    reserved.add(novoe_imya)\n",
    "    imena.append(novoe_imya)\n",
    "\n",
    "print([i.name for i in imena])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-04",
   "metadata": {},
   "source": [
    "## Проверка результата"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23-14-05",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert imena[0].name == \"otchet.pdf\"\n",
    "assert imena[1].name == \"otchet (1).pdf\"\n",
    "assert imena[2].name == \"otchet (2).pdf\"\n",
    "print(\"Верно: свободное имя находится по схеме name (n).ext.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-06",
   "metadata": {},
   "source": [
    "## Starter\n",
    "\n",
    "Заполните отмеченное место. Неизменённый starter не проходит tests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "task-23-14",
   "metadata": {
    "tags": [
     "exercise",
     "starter"
    ]
   },
   "outputs": [],
   "source": [
    "def svobodnye_imena(candidate: Path, count: int) -> list[Path]:\n",
    "    # TODO: maintain one reserved set and call _resolve_collision count times.\n",
    "    raise NotImplementedError\n",
    "\n",
    "\n",
    "imena2 = svobodnye_imena(papka.parent / \"images/photo.jpg\", 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-08",
   "metadata": {},
   "source": [
    "## Task\n",
    "\n",
    "Напишите функцию, последовательно резервирующую `count` безопасных имён для одного кандидата."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-09",
   "metadata": {},
   "source": [
    "## Tests\n",
    "\n",
    "Запустите после task cell: есть основной пример и хотя бы один крайний случай."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "tests-23-14",
   "metadata": {
    "tags": [
     "exercise-tests"
    ]
   },
   "outputs": [],
   "source": [
    "assert [p.name for p in imena2] == [\"photo.jpg\", \"photo (1).jpg\", \"photo (2).jpg\"]\n",
    "assert svobodnye_imena(Path(\"README\"), 1)[0].name == \"README\"\n",
    "assert svobodnye_imena(Path(\"x.txt\"), 0) == []\n",
    "print(\"Tests passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-11",
   "metadata": {},
   "source": [
    "## Hint\n",
    "\n",
    "После каждого результата добавляйте путь в `reserved` до следующего вызова."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-23-14-12",
   "metadata": {},
   "source": [
    "## Solution\n",
    "\n",
    "<details><summary>Показать решение после собственной попытки</summary>\n",
    "\n",
    "```python\n",
    "def svobodnye_imena(candidate: Path, count: int) -> list[Path]:\n",
    "    reserved = set()\n",
    "    result = []\n",
    "    for _ in range(count):\n",
    "        path = _resolve_collision(candidate, reserved)\n",
    "        reserved.add(path)\n",
    "        result.append(path)\n",
    "    return result\n",
    "\n",
    "\n",
    "imena2 = svobodnye_imena(papka.parent / \"images/photo.jpg\", 3)\n",
    "```\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Cartesian Python 3.14",
   "language": "python",
   "name": "cartesian-python314"
  },
  "language_info": {
   "name": "python",
   "version": "3.14.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
