Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit f11e326

Browse files
Handwritten README
1 parent 17227ae commit f11e326

2 files changed

Lines changed: 267 additions & 53 deletions

File tree

README.md

Lines changed: 248 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,267 @@
11
# Styx
22

3-
A configuration language that's actually pleasant to use.
3+
At least it's not YAML!
4+
5+
## Styx the straightforward
6+
7+
Imagine JSON
8+
9+
```json
10+
{
11+
"key": "value"
12+
}
13+
```
14+
15+
But you remove everything that's getting in the way: the double quotes, the
16+
the colon, even the comma:
17+
18+
```styx
19+
{
20+
key value
21+
}
22+
```
23+
24+
Of course you can have the comma back if you want to put everything in a single line:
25+
26+
```styx
27+
{key value, koi tuvalu}
28+
```
29+
30+
Not far enough? Wanna get rid of the brackets? Okay, but only for the top-level object:
431

532
```styx
6-
// Schema declaration - enables validation, completion, hover
7-
@ examples/server.schema.styx
33+
key value
34+
koi tuvalu
35+
```
36+
37+
What about arrays? They're called sequences and they use parentheses:
38+
39+
```styx
40+
methods (GET POST PUT)
41+
```
42+
43+
They're always whitespace-separated, never comma-separated.
44+
45+
## Styx the typed
46+
47+
```styx
48+
name "John Doe"
49+
age 97
50+
retired true
51+
```
52+
53+
Hey, which type are those values? Any type you want them to.
854

9-
/// The server's display name (this is a doc comment)
10-
name my-server
11-
port 8080
12-
enabled @true
55+
Scalars are just text atoms, `97` is not any more a number
56+
than `https://example.org/` is.
1357

14-
// Nested objects
15-
tls {
16-
cert /etc/ssl/cert.pem
17-
key /etc/ssl/key.pem
58+
Types matter at exactly two times:
59+
60+
- Validation via [schemas](https://styx.bearcove.eu/spec/schema/) (which are also Styx documents)
61+
- Deserialization, in either flavor (dynamic or static typing)
62+
63+
In dynamic typing flavor, your Styx document gets parsed into a tree,
64+
and then you get to request "field name as type string" — and if it can't
65+
be coerced into a string, you get an error at that point.
66+
67+
In static typing flavor, you may for example deserialize to:
68+
69+
```rust
70+
#[derive(Facet)]
71+
struct Does {
72+
name: String,
73+
age: number,
74+
retired: bool,
1875
}
76+
```
1977

20-
// Newlines or commas - your choice
21-
logging {level info, format {timestamp @true, colors @true}}
78+
And then the type mapping is, well, what you'd expect.
2279

23-
// Sequences
24-
allowed_methods (GET POST PUT DELETE)
80+
This solves the Norway problem:
2581

26-
// Tagged values for enums
27-
status @ok
28-
log_level @warn
29-
maybe_value @some(42)
82+
```styx
83+
country no
84+
```
85+
86+
This `no` is not a boolean, not a string, not a number, it's everything, everywhere,
87+
all at once, until you _need_ it to be something.
88+
89+
## Styx the nerd
90+
91+
Sometimes a value isn't quite enough, and you want to tag it:
92+
93+
```styx
94+
this (is an untagged list)
95+
that @special(list I hold dear)
96+
```
3097

31-
// Complex structures
32-
routes (
33-
@route {path /api/v1, handler api}
34-
@route {path /health, handler health_check}
98+
Remember `()` are for sequences. They're not for grouping/precedence/calls.
99+
100+
You can tag objects, too:
101+
102+
```styx
103+
rule @path_prefix{
104+
prefix /api
105+
route_to localhost:9000 // still no need to double-quote anything
106+
// oh yeah also comments just work
107+
}
108+
```
109+
110+
That's because Styx was designed to play nice with sum types,
111+
like Rust enums:
112+
113+
```rust
114+
enum Alternatives {
115+
NoPayload
116+
TuplePayload(u32, u32)
117+
StructPayload { name: String }
118+
}
119+
```
120+
121+
And so, tags are a natural way to _select_ a variant:
122+
123+
```styx
124+
alts (
125+
@no_payload@
126+
@tuple_payload(3, 7)
127+
@struct_payload{name Gisèle}
35128
)
129+
```
130+
131+
Did you notice the `@` at the end of `@no_payload@`? Not a typo:
132+
that's the unit value. It means "nothing", "none", kinda like "null"
133+
but a little superior.
134+
135+
`@` is a value like any other:
136+
137+
```styx
138+
sparse_seq (1 2 @ 8 9)
139+
```
140+
141+
And in fact, wanna know a secret? `@` is not even the canonical form
142+
of unit: `@@` is.
143+
144+
An empty tag degenerates to `@`, and a tag without a paylod defaults to
145+
a payload of `@`.
146+
147+
Therefore:
148+
149+
```styx
150+
@ // tag=@, payload=@ (implied)
151+
@@ // tag=@, payload=@
152+
@tag // tag=tag, payload=@ (implied)
153+
@tag@ // tag=tag, payload=@
154+
@tag"must" // tag=tag, payload=must
155+
@tag() // tag=tag, payload=() aka empty sequence
156+
```
157+
158+
Importantly, there is NEVER ANY SPACE between a tag and its payload.
159+
Spaces separate seq elements or key-value pairs in object context:
160+
161+
```styx
162+
// this is a key-value pair:
163+
@tag () // key(tag=tag, payload=@) value(tag=@, payload=())
164+
165+
// this is a DIFFERENT key-value pair
166+
@tag() // key(tag=tag, payload=()) value(tag=@, payload=@)
167+
```
168+
169+
Does it confusing? Maybe. Little bit.
170+
171+
## Styx the objective
172+
173+
We've just seen this in the last gotcha:
174+
175+
```styx
176+
@tag() // key(tag=tag, payload=()) value(tag=@, payload=@)
177+
```
178+
179+
Which, okay, `@tag()` is the entire key. But where's the value?
180+
181+
It's omitted. It defaults to `@`:
36182

37-
// Heredocs for multi-line content
38-
query <<SQL
39-
SELECT * FROM users
40-
WHERE active = true
41-
SQL
183+
```styx
184+
key @ // explicitly set to unit
185+
koi // implicitly set to unit
42186
```
43187

44-
## Features
188+
So, key-value pairs can be missing a value, and... they can also
189+
have more than one key.
190+
191+
```styx
192+
fee fi foe fum
193+
// equivalent to
194+
fee {fi {foe fum}}
195+
```
196+
197+
And that's /it/ with the weirdness. Some unfamiliar bits, but hopefully
198+
not too many, which lets us...
199+
200+
## Styx the schematic
201+
202+
...define Styx schemas in Styx itself.
203+
204+
```styx
205+
schema {
206+
/// The root structure of a schema file.
207+
@ @object{
208+
/// Schema metadata (required).
209+
meta @Meta
210+
/// External schema imports (optional).
211+
imports @optional(@map(@string @string))
212+
/// Type definitions: @ for document root, strings for named types.
213+
schema @map(@union(@string @unit) @Schema)
214+
}
215+
216+
// etc.
217+
}
218+
```
219+
220+
Are those doc comments? Yes. Parsers are taught to keep them and attach them to
221+
the next element. This means your styx documents can be validated against a
222+
schema:
223+
224+
* by a CLI, locally, in CI
225+
* by an LSP, in your code editor
226+
* honestly anytime for any reason
227+
228+
And that your code editor (mine's [Zed](https://zed.dev)) can have the full
229+
code editing experience: autocomplete, documentation on hover, jump to definition
230+
(in schema), hover for field documentation, etc.
231+
232+
It's... so nice.
233+
234+
## Styx the one last thing
235+
236+
Oh! Also, HEREDOCs:
237+
238+
```styx
239+
examples (
240+
{
241+
name hello.rs
242+
source <<SRC,rust
243+
fn main() {
244+
println!("Hello from Rust!")
245+
}
246+
SRC
247+
}
248+
)
249+
```
250+
251+
The `,rust` is just a hint which is used by your editor to inject syntax
252+
highlighting from the embedded language :)
253+
254+
## Implementations
255+
256+
There is a spec for parsing, schema validation, and error reporting,
257+
tracked with [Tracey](https://github.com/bearcove/tracey) and available
258+
on the [styx website](https://styx.bearcove.eu).
259+
260+
The flagship implementation is, of course, the Rust one — across multiple
261+
crates like `facet-styx` and `serde_styx`, but not just.
45262

46-
- **Schema validation** with helpful error messages
47-
- **Comments** that don't get lost
48-
- **Flexible syntax** - use newlines or commas, your choice
49-
- **Tags** for type annotations and enums (`@optional`, `@default`, custom types)
50-
- **LSP support** with completions, hover, go-to-definition, and more
263+
There's a TypeScript implementation in the repository, and probably more
264+
to come.
51265

52266
## Editor Support
53267

examples/nested.schema.styx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
meta {
2-
id https://example.com/nested-config
3-
version 2026-01-16
4-
description "Schema with nested objects for testing"
2+
id https://example.com/nested-config
3+
version 2026-01-16
4+
description "Schema with nested objects for testing"
55
}
6-
76
schema {
8-
@ @object{
9-
name @string
10-
server @object{
11-
host @string
12-
port @int
13-
tls @optional(@object{
14-
cert @string
15-
key @string
16-
ca @optional(@string)
17-
})
7+
@ @object{
8+
name @string
9+
server @object{
10+
host @string
11+
port @int
12+
tls @optional(@object{
13+
cert @string
14+
key @string
15+
ca @optional(@string)
16+
})
17+
}
18+
logging @optional(@object{
19+
level @string
20+
format @optional(@string)
21+
})
1822
}
19-
logging @optional(@object{
20-
level @string
21-
format @optional(@string)
22-
})
23-
}
2423
}
24+

0 commit comments

Comments
 (0)