Skip to content
/ styx Public
forked from bearcove/styx

STYX: at least it's not YAML

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

softdevca/styx

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

facet-styx

crates.io documentation MIT/Apache-2.0 licensed

Styx

At least it's not YAML!

Styx the straightforward

Imagine JSON

{
  "key": "value"
}

But you remove everything that's getting in the way: the double quotes, the colon, even the comma:

{
  key value
}

Of course you can have the comma back if you want to put everything in a single line:

{key value, koi tuvalu}

Not far enough? Wanna get rid of the brackets? Okay, but only for the top-level object:

key value
koi tuvalu

What about arrays? They're called sequences and they use parentheses:

methods (GET POST PUT)

They're always whitespace-separated, never comma-separated.

Styx the typed

name "John Doe"
age 97
retired true

Hey, which type are those values? Any type you want them to.

Scalars are just text atoms, 97 is not any more a number than https://example.org/ is.

Types matter at exactly two times:

  • Validation via schemas (which are also Styx documents)
  • Deserialization, in either flavor (dynamic or static typing)

In dynamic typing flavor, your Styx document gets parsed into a tree, and then you get to request "field name as type string" — and if it can't be coerced into a string, you get an error at that point.

In static typing flavor, you may for example deserialize to:

#[derive(Facet)]
struct Does {
    name: String,
    age: number,
    retired: bool,
}

And then the type mapping is, well, what you'd expect.

This solves the Norway problem:

country no

This no is not a boolean, not a string, not a number, it's everything, everywhere, all at once, until you need it to be something.

Styx the nerd

Sometimes a value isn't quite enough, and you want to tag it:

this (is an untagged list)
that @special(list I hold dear)

Remember () are for sequences. They're not for grouping/precedence/calls.

You can tag objects, too:

rule @path_prefix{
  prefix /api
  route_to localhost:9000 // still no need to double-quote anything
  // oh yeah also comments just work
}

That's because Styx was designed to play nice with sum types, like Rust enums:

enum Alternatives {
    NoPayload,
    TuplePayload(u32, u32),
    StructPayload { name: String },
}

And so, tags are a natural way to select a variant:

alts (
    @no_payload@
    @tuple_payload(3 7)
    @struct_payload{name Gisèle}
)

Did you notice the @ at the end of @no_payload@? Not a typo: that's the unit value. It means "nothing", "none", kinda like "null" but a little superior.

@ is a value like any other:

sparse_seq (1 2 @ 8 9)

And in fact, wanna know a secret? @ is not even the canonical form of unit: @@ is.

An empty tag degenerates to @, and a tag without a paylod defaults to a payload of @.

Therefore:

@            // tag=@,   payload=@ (implied)
@@           // tag=@,   payload=@
@tag         // tag=tag, payload=@ (implied)
@tag@        // tag=tag, payload=@
@tag"must"   // tag=tag, payload=must
@tag()       // tag=tag, payload=() aka empty sequence

Importantly, there is NEVER ANY SPACE between a tag and its payload. Spaces separate seq elements or key-value pairs in object context:

// this is a key-value pair:
@tag ()     // key(tag=tag, payload=@) value(tag=@, payload=())

// this is a DIFFERENT key-value pair
@tag()      // key(tag=tag, payload=()) value(tag=@, payload=@)

Is it confusing? Maybe. Little bit.

Styx the objective

We've just seen this in the last gotcha:

@tag()      // key(tag=tag, payload=()) value(tag=@, payload=@)

Which, okay, @tag() is the entire key. But where's the value?

It's omitted. It defaults to @:

key @ // explicitly set to unit
koi   // implicitly set to unit

So, key-value pairs can be missing a value. And dotted keys create nested structure:

server.host localhost
// equivalent to
server {host localhost}

And object attribute syntax provides a compact way to build objects:

{
    web domain>example.org      port>9000
    api domain>api.example.org  port>9001
}

And that's /it/ with the weirdness. (Don't worry, there are comprehensive specifications and test suites).

Some unfamiliar bits, but hopefully not too many, which lets us...

Styx the schematic

...define Styx schemas in Styx itself.

schema {
  /// The root structure of a schema file.
  @ @object{
    /// Schema metadata (required).
    meta @Meta
    /// External schema imports (optional).
    imports @optional(@map(@string @string))
    /// Type definitions: @ for document root, strings for named types.
    schema @map(@union(@string @unit) @Schema)
  }
  
  // etc.
}

Are those doc comments? Yes. Parsers are taught to keep them and attach them to the next element. This means your styx documents can be validated against a schema:

  • by a CLI, locally, in CI
  • by an LSP, in your code editor
  • honestly anytime for any reason

And that your code editor (mine's Zed) can have the full code editing experience: autocomplete, documentation on hover, jump to definition (in schema), hover for field documentation, etc.

It's... so nice.

Styx the one last thing

Oh! Also, HEREDOCs:

examples (
    {
        name hello.rs
        source <<SRC,rust
        fn main() {
          println!("Hello from Rust!")
        }
        SRC
    }
)

The ,rust is just a hint which is used by your editor to inject syntax highlighting from the embedded language :)

Implementations

There is a spec for parsing, schema validation, and error reporting, tracked with Tracey and available on the styx website.

The flagship implementation is, of course, the Rust one — across multiple crates like facet-styx and serde_styx, but not just.

There's a TypeScript implementation in the repository, and probably more to come.

Editor Support

Zed

Styx has first-class support for Zed with syntax highlighting, LSP integration, and more.

Documentation

See styx.bearcove.eu for full documentation.

Sponsors

Thanks to all individual sponsors:

GitHub Sponsors Patreon

...along with corporate sponsors:

Zed Depot

CI runs on Depot runners.

License

MIT OR Apache-2.0

Sponsors

Thanks to all individual sponsors:

GitHub Sponsors Patreon

...along with corporate sponsors:

AWS Zed Depot

...without whom this work could not exist.

License

Licensed under either of:

at your option.

About

STYX: at least it's not YAML

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 77.1%
  • TypeScript 7.8%
  • Swift 3.5%
  • Go 2.6%
  • Python 2.5%
  • Common Lisp 2.0%
  • Other 4.5%