Formaty plików konfiguracyjnych i metadanych ekosystemu HackerOS.
.hk jest głównym formatem konfiguracji — potężnym zamiennikiem YAML.
.hacker służy do opisu modułów i narzędzi w ekosystemie.
Czytelny, hierarchiczny format konfiguracji dla ekosystemu HackerOS. Zastępuje YAML wszędzie tam gdzie potrzebna jest jasna struktura, silne typowanie i wsparcie dla interpolacji.
Pliki .hk składają się z sekcji (odpowiedniki tabel w TOML) zawierających
klucze i wartości. Każda sekcja tworzy zagnieżdżoną mapę. Format jest czytelny dla ludzi,
obsługuje komentarze, wielopoziomowe zagnieżdżanie i interpolację zmiennych.
.hk jest używany jako główny plik konfiguracyjny
narzędzi, menedżera pakietów hpm oraz repozytoriów bibliotek HackerOS.! To jest komentarz — zaczyna się od '!' [metadata] -> name => Hacker Lang -> version => 1.5 -> stable => true -> tags => ["lang", "compiler", "hacker"] [build] -> output => "./dist" -> target => linux-x86_64 -> debug => false
| Element | Składnia | Opis |
|---|---|---|
| Komentarz | ! treść | Linia zaczynająca się od ! jest ignorowana |
| Sekcja | [nazwa] | Najwyższy poziom hierarchii, tworzy mapę klucz-wartość |
| Klucz L1 | -> klucz => wartość | Klucz na pierwszym poziomie zagnieżdżenia |
| Klucz L2 | --> klucz => wartość | Klucz na drugim poziomie zagnieżdżenia |
| Klucz L3 | ---> klucz => wartość | Klucz na trzecim poziomie (i głębiej) |
| Mapa inline | -> nazwa_mapy | Linia bez => — tworzy podmapy |
| Klucz kropkowy | -> a.b.c => val | Skrót tworzący zagnieżdżone mapy: a → b → c |
_, - i . (kropki
tworzą automatyczne zagnieżdżenie). Klucz w cudzysłowach ("klucz") jest traktowany
jako literał — kropki wewnątrz nie powodują zagnieżdżenia.Parser automatycznie wykrywa typ wartości. Dostępne typy:
Z cudzysłowami lub bez. Wersja cytowana obsługuje sekwencje ucieczki:
\n \t \r \" \\
Liczby całkowite i zmiennoprzecinkowe. Parsowane jako f64. Przykłady: 42, 3.14, -1.5
Wartości true / false (bez rozróżniania wielkości liter). Np. True, FALSE są poprawne.
Lista wartości oddzielonych przecinkami w nawiasach kwadratowych. Elementy mogą być różnych typów. Np. [1, "dwa", true]
Zagnieżdżona struktura klucz-wartość. Tworzona przez podwójne strzałki lub klucze z kropkami.
[types] -> tekst_plain => hello world -> tekst_quoted => "hello, world\nwith newline" -> liczba_int => 42 -> liczba_float => 3.14 -> flaga => true -> tablica => [1, 2.5, true, "cztery"]
Głębokość zagnieżdżenia określa liczba myślników przed >.
Każdy dodatkowy myślnik to jeden poziom głębiej. Alternatywnie można użyć
kropek w kluczu jako skrótu.
[libraries] ! Mapa L1: "obsidian" -> obsidian ! Klucze L2 wewnątrz "obsidian" --> version => 0.2 --> description => Biblioteka inspirowana zenity. --> authors => ["HackerOS Team <hackeros068@gmail.com>"] --> so-download => https://github.com/Bytes-Repository/obsidian-lib/releases/download/v0.2/libobsidian_lib.so -> yuy --> version => 0.2 --> description => Twórz ładne interfejsy CLI
[config] ! Tworzy config → database → host -> database.host => localhost -> database.port => 5432 -> database.ssl => true ! Trzy poziomy głębokości -> server.tls.cert => /etc/ssl/cert.pem -> server.tls.key => /etc/ssl/key.pem
-> a => 1 a potem -> a.b => 2 spowoduje błąd KeyConflict.
Wartości tekstowe mogą zawierać referencje do innych kluczy lub zmiennych środowiskowych,
używając składni ${...}. Parser automatycznie rozwiązuje referencje, wykrywa cykle i błędne odwołania.
[project] -> name => MyApp -> version => 1.0 [paths] ! Referencja do klucza z innej sekcji -> base => /opt/${project.name} -> config => ${paths.base}/config -> logs => ${paths.base}/logs ! Zmienna środowiskowa (prefix "env:") -> home => ${env:HOME} -> user => ${env:USER} [data] -> numbers => [10, 20, 30] ! Indeksowanie tablicy -> first => ${data.numbers[0]}
| Składnia | Przykład | Opis |
|---|---|---|
| ${sekcja.klucz} | ${project.name} | Referencja do klucza w innej sekcji |
| ${sekcja.mapa.klucz} | ${build.opts.level} | Referencja głęboko zagnieżdżona |
| ${sekcja.tablica[n]} | ${data.list[0]} | Indeksowanie elementu tablicy (od 0) |
| ${env:NAZWA} | ${env:HOME} | Zmienna środowiskowa; pusta jeśli nieustawiona |
Parser zwraca precyzyjne błędy z numerem linii i kolumny. Każdy błąd ma wbudowany hint wyświetlany w terminalu.
Błąd składni. Zawiera numer linii i kolumny oraz opis problemu. Terminal pokazuje wskaźnik ^ w odpowiednim miejscu.
Próba odczytu wartości jako nieodpowiedni typ. Np. odczyt liczby z klucza który jest mapą.
Interpolacja wskazuje na nieistniejący klucz. Sprawdź, czy ścieżka sekcja.klucz istnieje.
Dwie wartości interpolują się nawzajem. Np. a => ${b} i b => ${a}.
Duplikat klucza lub konflikt między kluczem prostym a kluczem z kropką wskazującym to samo miejsce.
Wymagane pole nie zostało znalezione podczas deserializacji struktury.
error: parse error → at 4:3 -< klucz => wartość ^ Expected '>' after dashes Hint: Try: key => value
[server] -> host => localhost -> port => 8080 -> ssl => true -> tls --> cert => /etc/cert.pem --> key => /etc/key.pem -> allowed => ["GET", "POST"]
server:
host: localhost
port: 8080
ssl: true
tls:
cert: /etc/cert.pem
key: /etc/key.pem
allowed:
- GET
- POST
Zagnieżdżenie przez myślniki — nie przez spacje. Brak błędów wynikających z tabów vs spacji.
Natywne ${ref} i ${env:VAR} bez zewnętrznych narzędzi. YAML nie ma tego wbudowanego.
Błąd wskazuje linię, kolumnę i daje hint naprawczy. YAML-owe błędy bywają kryptyczne.
Parser używa IndexMap — kolejność kluczy w pliku jest zachowana przy serializacji.
Lekki format metadanych dla modułów, aplikacji i narzędzi w ekosystemie HackerOS. Obsługuje trzy wersje o różnym poziomie szczegółowości.
Każdy plik .hacker to zawartość ujęta w nawiasy kwadratowe [ ... ].
Parser automatycznie wykrywa wersję na podstawie struktury zawartości.
Wersja 1 to fallback — dowolny tekst między nawiasami. Używana np. do oznaczenia numeru wersji lub krótkiego opisu.
[ 0.2 ] ! lub wieloliniowo: [ dowolny tekst w kilku liniach ]
Wersja 2 zawiera nagłówek i sekcje z listami wartości. Używana np. w plikach repozytorium opisujących dostępne kernele, narzędzia, wersje.
[ Nazwa projektu ! nagłówek — linie przed pierwszą sekcją jadro: ! nazwa sekcji + dwukropek = xanmod ! wartość sekcji — prefix "= " = liquorix narzedzia: = hpm = hl wersje: = 0.1 = 0.2 = 1.1 ]
Wersja 3 opisuje konkretny moduł lub aplikację przez cztery wymagane pola. Używana jako główny plik opisu aplikacji HackerOS.
[ = application ! nagłówek (= ) => gui ! typ aplikacji (=> ) -> aplication for cybersecurity ! opis (-> ) --> main.hacker ! ścieżka pliku (--> ) ]
| Prefix | Pole | Opis |
|---|---|---|
| = | header | Nazwa / nagłówek modułu |
| => | type | Typ: gui, cli, lib, itd. |
| -> | description | Krótki opis modułu |
| --> | file | Ścieżka do głównego pliku |
Parser próbuje wersje w kolejności: v2 → v3 → v1.
V1 jest zawsze akceptowany jako fallback — jeśli plik nie pasuje do v2 ani v3,
cała zawartość trafia do pola content.
.hacker musi zaczynać się od [ i kończyć na ] (z opcjonalnymi białymi znakami).
Pusty plik ([]) zwraca błąd EmptyContent.
Publiczne funkcje eksportowane przez crate hk-parser i hacker-parser.
.hk. Zwraca HkConfig (IndexMap sekcji).BufReader wewnętrznie.${...} w miejscu. Modyfikuje config. Wykrywa cykle.HkConfig z powrotem do stringa w formacie .hk. Zachowuje kolejność.HkValue — metodyString. Błąd dla Array/Map.f64. Błąd jeśli wartość nie jest typu Number.bool. Błąd jeśli wartość nie jest typu Bool.Vec<HkValue>. Błąd jeśli to nie tablica.IndexMap<String, HkValue>. Błąd jeśli to nie mapa.use hk_parser::{load_hk_file, resolve_interpolations}; fn main() -> Result<(), Box<dyn std::error::Error>> { let mut config = load_hk_file("config.hk")?; // Rozwiąż interpolacje (env vars, referencje) resolve_interpolations(&mut config)?; // Odczyt zagnieżdżonej wartości let server = config["server"].as_map()?; let host = server["host"].as_string()?; let port = server["port"].as_number()? as u16; println!("Connecting to {}:{}", host, port); Ok(()) }
.hacker. Zwraca enum z wariantem V1, V2 lub V3..hacker.use hacker_parser::{parse_file, HackerFile}; let file = parse_file("app.hacker")?; match file { HackerFile::V1(v) => println!("v1 content: {}", v.content), HackerFile::V2(v) => { println!("header: {}", v.header); for (section, values) in &v.sections { println!(" {}: {:?}", section, values); } } HackerFile::V3(v) => { println!("app: {} ({})", v.header, v.ty); println!("desc: {}", v.description); println!("entry: {}", v.file); } }