Как создать модуль Nova
Общий гайд: от пустого каталога до публикуемого пакета. Native-backed модуль (обёртка над
.c/готовой.lib) — частный случай в конце (§7): native-модуль обязан собираться БЕЗ Rust/cargo — только.nv+ опционально.c(компилит clang) + опционально готовая.lib/.a(линкуется, не собирается).Смежные документы (не дублируются здесь, а до-читываются по ссылке): module-conventions (дизайн модуля: эффект-плумбинг, value/must-consume-типы, домен ошибок), nv-coding-style (стиль
.nv-кода), ffi-cookbook (механика FFI:extern "C", указатели,CStr,[ffi]), spec D78 (нормативные правилаnova.toml/ module-path).
0. TL;DR
- Создай каталог; положи в корень
nova.tomlс[package] name. - Пиши
.nv-файлы — путь файла = путь модуля (foo/bar.nv⇒module foo.bar). - Тесты — рядом, в файлах
*_test.nv(илиtest "…" { }-блоки внутри модуля). - Публичную поверхность помечай
export+#stable(since = "X"). - Нужны C-артефакты — задекларируй их в
[ffi](готовый.c-шим + опционально готовая.lib/.a); при импорте модуля они соберутся и слинкуются автоматически (§7).
1. Layout пакета
Пакет — это каталог с nova.toml в корне. Source root = корень пакета
(отдельного src/ нет — D78, 2026-05-22). Модули лежат прямо в подкаталогах:
nova-greet/ repository: nova-<package> (§8)
├── nova.toml manifest (required)
├── LICENSE
├── README.md
├── greet.nv module greet (the package's root module)
├── greet_test.nv tests alongside the module
└── format/
├── ascii.nv module format.ascii
└── ascii_test.nv tests alongside
Служебные каталоги (target/, .git/, скрытые .-префикс) резолвер
пропускает. Не-.nv каталоги (assets/, docs/) модулями не считаются.
2. nova.toml — манифест
Минимум — [package] name; version желателен. Полная схема — D78.
[package]
name = "greet" # snake_case (D30); the package name = the modules' prefix
version = "0.1.0" # semver
nova-version = "0.5" # minimum Nova version
description = "Greetings in different languages"
license = "MIT OR Apache-2.0" # SPDX
repository = "https://github.com/you/nova-greet"
[[bin]] # optional: a binary entry point
name = "greet"
path = "bin/greet.nv"
[dependencies] # optional: external packages
some-lib = "1.2" # from the registry
internal = { path = "../internal" } # local
remote = { git = "https://github.com/…", tag = "v1" } # git (Plan 03.1/03.2)
Пакет по умолчанию — библиотека: его export-декларации импортируемы
другими пакетами без какой-либо [lib]-секции. [[bin]] добавляет бинарные
точки входа (пакет может быть и библиотекой, и набором бинарей).
3. Module path = file path (D78)
Компилятор обязательно сверяет объявление module … с путём файла;
несоответствие — E_D78_MODULE_PATH_MISMATCH с подсказкой. Правило (rev-3):
Файл (от корня пакета greet) | Объявление | Импорт |
|---|---|---|
greet.nv | module greet | import greet.{hello} |
format/ascii.nv | module format.ascii | import format.ascii.{…} |
format/ascii/upper.nv (peer папки) | module format.ascii | — |
Папка = ОДИН модуль из равноправных файлов (файлы одного модуля делят одно
объявление parent.folder). Файл и папка одного имени в одном каталоге
запрещены.
4. Публичная поверхность и стабильность
export— то, что видно снаружи модуля/пакета; безexportэлемент module-private. Межпакетный импорт — только черезexport(D216-ecosystem).#stable(since = "X")на каждом публичном элементе — semver-контракт. Для библиотек это можно сделать обязательным:[lib] enforce-stability = trueпревращает отсутствие маркера в ошибкуnova doc --check(D127).- Незрелый API —
#unstable/#experimentalвместо#stable.
module greet
#stable(since = "0.1")
export fn hello(name str) -> str => "Hello, ${name}!"
5. Тесты рядом с модулем
Тесты живут рядом с модулем — в файлах *_test.nv (исключаются из
release-графа) либо test "…" { }-блоками внутри самого модуля. Не складывать
тесты в отдельное дерево. Классификация pos/neg — по EXPECT_*-маркеру, не по
папке (test-conventions).
module greet
test "hello inserts the name" {
assert(hello("Ada") == "Hello, Ada!")
}
Для эффект-модулей (§6) обязателен mock-handler-тест — детерминизм без реального ресурса.
6. Дизайн модуля (кратко; полное — module-conventions)
Для I/O-, OS- и ресурсных подсистем канон Nova — эффект-плумбинг + фасад на типах (module-conventions):
- Эффект — внутренняя dispatch-точка (
type Fs effect { … }); юзер его не зовёт напрямую → мокабельность (with Fs = mem_fs() { … }). - User-API — методы на типах + free-fns (
File.open(path)), эффект виден в effect-row сигнатуры, а не в имени опа. - Мелкие значения —
value-record; ресурсы — must-consume@close() -> Result. - Ошибки — один структурный
XError { kind, … }+ OPENErrorKind. - byte-first: сырой I/O —
[]u8;strтолько черезfrom_utf8 -> Result.
Чистая алгоритмика (парсинг, кодировки, календарь) — обычные .nv-функции без
эффекта.
7. Native-backed модуль (частный случай)
Модуль может стоять поверх C-библиотеки или Rust-крейта. Тонкий слой FFI
(extern "C" fn, типы-хендлы, CStr, указатели, ABI) — целиком в
ffi-cookbook; здесь — только как подключить артефакты
к сборке, чтобы import модуля тянул их автоматически.
Native-зависимость декларируется в nova.toml через единственную секцию:
7.1. Готовые .c-шимы и системные .lib — [ffi]
[ffi]
c_shims = ["native/sqlite3_shim.c"] # compiled and linked
include_dirs = ["native/", "third_party/sqlite3/"] # clang -I
libs = ["sqlite3"] # system: clang -lsqlite3 / sqlite3.lib
Если системная .lib не в стандартном search-path (vcpkg-триплет, vendored
копия) — линковка резолвится и подключается прямо в build-пайплайне
(test_runner.rs::build_command), тем же условным-по-факту-использования
паттерном D337, что у brotli/net.c. Native-модуль никогда не требует
построить native-артефакт (cargo build, make) как часть своей
сборки — только слинковать готовый; эталон паттерна — std/tls в монорепо
(nova_rt/tls_c_shim.c + vcpkg mbedTLS, mbedTLS ставится ЗАРАНЕЕ через
vcpkg install, безо всякой манифест-декларации). Полная механика —
ffi-cookbook §Build pipeline.
8. Именование и публикация (внешний пакет)
Конвенция для внешних (в т.ч. native-backed) пакетов — D78-амендмент, Plan 195:
| Сущность | Конвенция | Пример |
|---|---|---|
| Репозиторий | nova-<пакет> | nova-tls |
Имя пакета ([package] name) | <пакет> | tls |
| Корень модуля | <пакет>.* | import tls.{TlsStream} |
| Native-артефакты | native/ | native/tls_shim/ |
Публикация: закоммить пакет в репозиторий nova-<пакет>; потребитель
подключает его как git-зависимость —
[dependencies] tls = { git = "https://…/nova-tls", tag = "v0.1.0" }. Реестр
(named <пакет> = "1.2") — Plan 03.3, отдельно.
9. Чек-лист нового модуля
nova.tomlс[package] nameв корне..nv-файлы:module path = file path; папка = один модуль.- Публичное —
export+#stable(since); для либы —enforce-stability = true. - Тесты рядом (
*_test.nv/test-блоки); эффект-модуль → mock-тест. - Дизайн по module-conventions (эффект-плумбинг + фасад; value/must-consume; один
XError). - Native —
[ffi](готовые.c-шимы + готовая.lib/.a); шага сборки через Rust/cargo как части сборки модуля нет. - Внешний пакет — репо
nova-<пакет>, native вnative/, git-зависимость.