Это статья рассказывает об одном пункте лайфлиста — списка из 100 клёвых вещей, которые я хочу попробовать.
Мне давно хотелось что-нибудь написать на Rust'е. И здесь я подробно рассказываю о первом опыте.
Зачем нужен «Типограф»?
«Типограф» — это сервис, написанный «Студией Лебедева». Он помогает делать текст легко читаемым:
- расставляет кавычки «ёлочки»;
- меняет дефисы на длинные тире;
- расставляет символы неразрывных пробелов, чтобы не получалось «висячих» предлогов в конце строки;
- ...и, наверняка, делает ещё много полезного.
Артемий Лебедев написал о правилах типографики, которые легли в основу сервиса в 62-м параграфе Ководства.
Я пользуюсь им всегда, когда пишу в блог.
У Типографа есть веб-версия, но мне не хотелось запускать браузер каждый раз, когда что-то нужно оттипографировать. Поэтому я решил сделать утилиту командной строки и получать красивый текст сразу, как только сохранил его.
Почему Rust?
Rust мне любопытен по трём причинам: из-за статической типизации, интересных хаскелоподобных конструкций, плюс новой для меня концепции владения-заимствования.
№ 1. Статическая типизация
Я кайфую, когда язык задаёт жесткие правила игры, и просто нельзя вернуть из int
-овой функции None
, String
или обвалиться с исключением, никак не отразив это в типе функции.
Это есть в Rust, это есть в Haskell, это есть в Swift.
Написание кода похоже на детскую игру «Геометрический сортер»:
«Раз у меня в руках треугольник, а тут квадратная прорезь — значит нужно взять переходник из треугольников в квадраты».
№ 2. Это как новый Haskell
Потому что:
- Ругается на косяки ещё до рантайма (см. предыдущий пункт).
- В Rust есть тип Option, похожий на Maybe из Haskell.
- Там есть паттерн-мэтчинг.
- Там есть трейты.
Любой из этих пунктах можно найти в других языках программирования (а тип Option
есть даже в C++). Но когда все они встречаются в одном языке, такой язык я воспринимаю крайне положительно и сразу хочу написать Типограф :)
Новый ли это Haskell? — нет, это шутка.
Но мне, определенно, нравится, что элменты дизайна, привычные для функционального программирования, появились в языке, рассчитанном на скрупулезную работу с памятью.
№ 3. Владение и заимствование
Это «фишка» языка и совершенно новая концепция для меня. Она мне показалась интересной.
Рекомендую почитать официальные доки про владение и заимствование, они совсем не сложны для понимания и весьма полезны.
Если хотите обсудить что-то из этих трёх пунктов или добавить ещё что-нибудь, добро пожаловать в комменты :)
Постановка задачи
Написать программу, чтобы:
- больше не надо было ходить в веб-версию;
- можно было типографировать «на лету» — при нажатии Ctrl+S или автосохранении в текстовом редакторе.
Реализация
Кодом поделился на гитхабе. Буду рад, если риквестуете фичу или найдете баг.
Общие впечатления
В принципе ничего особенно сложного в процессе кодирования не было: я взял за основу реализацию на Python, выложенную на сайте студии.
Там всё очень просто:
- Сформировали XML-запрос с помощью конкатенации.
- Открыли сокет.
- Отправили запрос в сокет.
- Прочитали из сокета ответ.
Из интересного — добавил настройку для pre-commit, которую хочу использовать в других проектах: https://github.com/nskeip/typograf-client/blob/master/.pre-commit-config.yaml.
Какие редакторы попробовал:
- VS Code.
- PyCharm (вообще подойдет любая IDE от JetBrains).
- CLion.
Все работают здорово, никаких проблем не было замечено. В платный CLion пересел только потому что там дебаггер, к которому я привык, и лень было разбираться с консольным (а надо бы).
Что добавил
Чего не было в прототипе и появилось в моей версии:
- Параметры командной строки с помощью structopt.
- Возможность редактировать текст in-place, а не только выводить в stdout.
- Возможность работать с форматом Mardown для блогового движка Zola — не типографировать блок мета-данных в начале файла.
Косяки
Ну, как без них.
- С непривычки: создал строку для чтения файла в неё, начал во всю использовать, но забыл поместить в нее содержимое файла. Здесь сразу не понял, в чем дело — только дебаггер помог :)
- Не закрыл тег при приготовлении XML. Вот она, конкатенация! Но библиотеку для «правильного» формирования XML здесь добавлять не хочется, так как не хочется раздувать размер бинарника.
- Обсчитался при работе с
File.seek
иFile.set_len
, когда делал опцию-s
для Zola.
Тут не заметил, там не закрыл — вполне естественные штуки, когда пишешь на новом языке. Считаю, что в отношении ошибок и быстрого их исправления опыт был весьма позитивным.
Полировка
В итоге понял, что работает не так, как мне надо:
- в конце строк добавлялись
<br>
, - «пули» в списках заменялись на что-то вроде — .
Это было избыточно для меня, потому что портило Markdown. Если бы я готовил текст в формате HTML, то это было бы норм.
Добавлять новые ключи не хотелось, чтобы не перегружать программку опциями из-за одного пользователя :)
Тем более что есть sed и годная статья на тему https://thoughtbot.com/blog/sed-102-replace-in-place.
В итоге я написал пару регулярок на sed, чтобы удалять избыточные <br>
и приводить в порядок списки.
Заключение
«А как же сохранение по нажатию Ctrl+S?» — спросите вы.
Всё нормально, оно работает.
Я не стал шаманить его прямо в программе, как и тот костыль с sed’ом. Вместо этого я сделал Makefile, который помогает оттипографировать нужный мне файл командой вида make typograf TARGET=filename.md
:
typograf:
/path/to/typograf-client -i -s --entity-type=1 --use-br 1 $(TARGET)
sed -i 's/<br \/>//' $(TARGET)
sed -i 's/^— / - /' $(TARGET)
А для того, чтобы типографировать сразу, как только файл изменился, я пользуюсь утилитой entr
и указываю, за каким файлом нужно следить:
find -type f -name '*.md' | entr make typograf TARGET=./content/100/rust.md
Есть два важных момента:
- Редактор должен поддерживать перезагрузку файла с диска при его изменении.
- Команду нужно изменять под каждый новый файл — тут надо просто допрограммировать.
UPDATE: И ещё один момент. Очень жаль, но Типограф делает ошибки в сложных случаях — он не любит, когда вы вручную вставляете последовательность вида &что-нибудь. Тогда типографировать лучше в самом конце, затем вручную исправлять ошибки бездушного робота, и уже его не запускать, иначе он всё опять сломает. В таких ситуациях автоматический запуск при сохранении больше мешает, чем помогает.
Главное, что я действительно упростил себе жизнь и теперь занят только работой над текстом, а типографируется он сам собой.