liza-text: приложение командной строки
Реализовано в 0.0.24. liza-text проверяет текстовые файлы и считает
Unicode scalar values, читая файл до EOF чанками по 2 КиБ.
Аргументы, пути и содержимое файла имеют разные контракты: путь не обязан
быть корректным UTF-8, даже когда проверяемый файл должен им быть.
Сборка и запуск
Из корня репозитория:
python3 -m liza build examples/apps/liza_text.liza -o build/liza-text
./build/liza-text check examples/data/utf8.txt
./build/liza-text count examples/data/utf8.txt
./build/liza-text info examples/data/utf8.txt
На Windows результат — build/liza-text.exe; запуск в PowerShell:
Можно сразу собрать и запустить через компилятор. Всё после --
передаётся исполняемой программе, без повторной интерпретации оболочкой:
Программа принимает ровно команду и путь. - считается обычным именем
файла; stdin, рекурсивный обход, wildcard expansion и дополнительные флаги
в этот инструмент не входят. Оболочка может сама раскрыть шаблон до запуска.
Точный вывод
Для файла Лиза 😀 с завершающим LF:
| Команда | stdout | stderr | Код |
|---|---|---|---|
check |
OK\n |
Пусто | 0 |
count |
7\n |
Пусто | 0 |
info |
Bytes: 14\nCode points: 7\n |
Пусто | 0 |
Здесь \n обозначает один байт LF, включая перенаправленный вывод Windows.
info считает байты содержимого файла, без платформенного перевода строк.
NUL и BOM допустимы и учитываются; счётчик измеряет кодовые точки, не графемы.
Пустой файл даёт 0 байт и 0 кодовых точек.
При некорректном UTF-8 все три команды оставляют stdout пустым и пишут в stderr:
Смещение начинается с нуля и указывает на начало некорректной последовательности относительно файла, даже если она пересекает границу чтения.
| Код | Значение | Диагностика в stderr |
|---|---|---|
0 |
Успех | Нет |
1 |
Некорректный UTF-8 | Invalid UTF-8 at byte N\n |
2 |
Неверная команда или число аргументов | Usage: liza-text <check\|count\|info> <file>\n |
3 |
Ошибка ввода-вывода | Cannot open file\n, Cannot read file\n или Cannot close file\n; при ошибке самого вывода сообщение не гарантируется |
4 |
Превышение поддерживаемого размера u64 |
Input exceeds supported size\n |
Успешный результат выводится только после EOF, закрытия файла и успешного
finish. Если само чтение или закрытие не удалось, результат проверки текста
не выводится. Ошибка чтения/подсчёта размера имеет приоритет над последующей
ошибкой закрытия. Ошибка записи диагностики меняет код завершения на 3.
При отказе записи stdout или stderr уже принятый ОС префикс остаётся в потоке:
транзакционность вывода не обещается.
Аргументы и lifetime
cli.count() // u32, эффект io
cli.arg(index) // Option<Arg>, эффект io
cli.path_arg(index) // Option<Path>, эффект io
cli.equals(arg, "check") // bool, pure
index имеет тип u32. Индекс 0 — имя программы, переданное средой запуска;
это не гарантия абсолютного пути к исполняемому файлу. Отсутствующий индекс,
включая 4294967295, возвращает None. Пустой аргумент
отличается от отсутствующего. Получение аргументов имеет эффект io;
сравнение уже полученного неизменяемого аргумента — pure.
Arg и Path — два различных непрозрачных типа. Они заимствуют неизменяемые
данные запуска, доступные до возвращения из main. Их можно копировать,
перемещать, передавать и возвращать между функциями, в том числе generic.
copy копирует только заимствованное представление, не создаёт собственного
хранилища строки. Пользователь ничего не освобождает.
В 0.0.24 они не могут быть полями пользовательских структур/вариантов,
элементами массивов, views, региональных буферов или Vec. Option и Result
могут переносить их между функциями, но обёртка не снимает запрет размещения
в таких агрегатах. Возвращаемый тип main по-прежнему u32: заимствование не
выходит за время жизни аргументов процесса.
Нет конструкторов Arg(...)/Path(...), доступа к полям, преобразований
в число, Text, View<u8> или произвольный адрес. Не вводятся String,
конкатенация, нормализация и изменение аргументов.
cli.equals сравнивает целиком, с учётом регистра, без нормализации и locale:
на POSIX — с UTF-8 байтами литерала, на Windows — с его UTF-16 представлением.
Некорректный для Unicode аргумент не мешает использовать его как путь.
Литерал с встроенным NUL не совпадает ни с одним аргументом.
Путь остаётся платформенным
match cli.path_arg(2) {
None => { return 2 }
Some(path) => {
match file.open_path(path) {
Err(error) => { return 3 }
Ok(input) => {
match file.close(move input) {
Ok(closed) => { return 0 }
Err(error) => { return 3 }
}
}
}
}
}
Этот фрагмент — тело функции с эффектом io.
file.open_path(Path) -> Result<File,FileError> открывает файл только для
чтения. Полученный File — самостоятельный ресурс с прежними обязанностями
закрытия. Существующий file.open(Text) для литералов не изменён.
| Платформа | Представление и открытие |
|---|---|
| Linux/macOS | Исходные NUL-terminated байты argv передаются в open, без проверки UTF-8 |
| Windows | UTF-16 code units аргументов wmain передаются в CreateFileW, без ANSI code page и UTF-8 round trip |
Файловая система может отклонить имя по собственным правилам. Например,
не-UTF-8 имя проверяется реальным файлом на Linux; файловая система macOS
может отвергать такие имена. На Windows сохраняются и непарные UTF-16
суррогаты, если файловая система допускает имя. Пустой путь возвращает
FileError.InvalidPath; ограничения длины и доступа определяет ОС.
На Windows используется широкая точка входа wmain.
Подготовку аргументов выполняет CRT перед входом в программу; эта работа
может включать выделение памяти. Она не объявляется бесплатной и не входит
в эффект отдельного вызова cli.arg. После инициализации cli.count,
cli.arg и cli.path_arg не создают строк и не выделяют память.
Открытие пути не копирует содержимое файла и не перекодирует Path.
stdout и stderr
Работающие функции stdout сохранены. Для stderr добавлены:
| stdout | stderr |
|---|---|
console.write(Text) |
console.err(Text) |
console.println(Text) |
console.errln(Text) |
console.print_u64(u64) |
console.err_u64(u64) |
console.print_i64(i64) |
console.err_i64(i64) |
console.write_bytes(View<u8>) |
console.err_bytes(View<u8>) |
Все возвращают Result<u64,IoFailure> и требуют io. Успех означает,
что переданные данные приняты потоком ОС, не физическую запись на диск и
не гарантию отображения пользователем. При ошибке written сообщает
успешно записанный префикс в байтах. errln добавляет LF отдельной записью.
На Windows текст терминала записывается через WriteConsoleW, а файл или
pipe — через WriteFile с UTF-8 байтами. Выбор делается по handle каждого
потока отдельно, согласно контракту Windows Console.
Кодовая страница процесса не меняется. Raw bytes не перекодируются.
Исходник приложения
Разбор аргументов, цикл чтения, счётчики, обработка Result и формат вывода
написаны на Liza. Платформенная часть ограничена получением аргументов,
открытием пути и записью в поток. Динамического String для приложения не
потребовалось.
Проверки O0/O2 покрывают точные stdout/stderr и коды завершения, Unicode и
пробелы в путях, нестандартные платформенные имена, ошибки чтения/закрытия,
broken pipe и файлы больше 2 КиБ. Переполнение проверяется без огромного
файла через граничные счётчики. Отдельно проверяется отсутствие аллокаций,
копирования строк и перекодирования в тракте получения аргумента и открытия
Path; внутренние затраты CRT и ОС не входят в эту проверку.