Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions runtime/conformance/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ publish = false

[dependencies]
comline-runtime = { path = "../core" }
# The specification's catalog, from the vendored copy (spec/rust), so it is
# always the revision spec/REV pins: the fact names and values the profiles
# use.
comline-catalog = { path = "spec/rust" }
serde = { version = "1.0.190", features = ["derive"] }
serde_json = "1.0.108"

Expand Down
6 changes: 6 additions & 0 deletions runtime/conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ runtime/conformance/vendor-spec.sh --check # CI: spec/ still matches spec/REV

`runner::SPEC_VERSION` is the vendored `spec/VERSION`.

The specification's catalog of names comes with it, as the `comline-catalog`
crate in `spec/rust`. This crate depends on it by path, so it's always the
revision `spec/REV` pins: the profiles' fact names and values are held to it
(`facts_are_the_catalogs`), and `comline-runtime`'s wire-name constants are
too, from a dev-dependency (`runtime/core/tests/catalog.rs`).

What stays here is the Rust runtime's own: [`support/rust.json`](support/rust.json)
and [`reports/rust.json`](reports/rust.json). [`check.sh`](check.sh) vets them
against the vendored specs.
Expand Down
3 changes: 2 additions & 1 deletion runtime/conformance/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ program has to agree on: the guarantees, the facts and the values each may
take, the official encodings with their aliases, and the wire features, with
the sets they form. The profiles' facts and `features.json` come from it, and
[`catalog/gen/catalog.json`](catalog/gen/catalog.json) holds it as JSON for
readers without CUE. See [the proposal](https://github.com/ComlineProject/docs/pull/162)
readers without CUE, and the `comline-catalog` crate ([`rust/`](rust)) as
Rust constants. See [the proposal](https://github.com/ComlineProject/docs/pull/162)
and [core#219](https://github.com/ComlineProject/core/issues/219) for what
will read it.

Expand Down
2 changes: 1 addition & 1 deletion runtime/conformance/spec/REV
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f21985277f65ca2af1fd9c78a0cf2eac0e1596bc
69d88327431275017ccce99fec43060467515a9f
15 changes: 11 additions & 4 deletions runtime/conformance/spec/catalog/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@ What an entry may say, and the rules across entries, are in
their values from here, so a profile is held to the catalog's domains.
- [`features.json`](../features.json) is generated from `features`.
- [`gen/catalog.json`](gen/catalog.json) is the whole catalog as JSON, for
readers without CUE: the compiler, the runtimes, generators, the language
server, the Observatory and the docs.
readers without CUE: the Observatory, the docs, generators in other
languages.
- The **`comline-catalog`** Rust crate ([`rust/`](../rust)) holds it as
constants and tables, `no_std` and without dependencies: core, the runtime
and the language server depend on it by git rev. Its types and lookups are
written by hand in `rust/src/lib.rs`; its data, `rust/src/catalog.rs`, is
generated by [`rust.cue`](rust.cue). An id's constant is its name in upper
case, with anything but letters and digits as `_`: `packed@1` is
`encoding::PACKED_1`.
- `specs/check.sh` checks that `profiles/` holds exactly the encodings with a
profile, each of its encoding's kind, and `vectors/` exactly the features.

Expand All @@ -43,8 +50,8 @@ A third party's encoding isn't an entry here: its package declares it in
## Changing it

Edit the `.cue` files, then run `specs/check.sh` to regenerate
`features.json` and `gen/catalog.json`. CI runs `specs/check.sh --check`,
which fails when either is stale.
`features.json`, `gen/catalog.json` and `rust/src/catalog.rs`. CI runs
`specs/check.sh --check`, which fails when any of them is stale.

Order matters where it shows: facts are listed in the order profiles list
them, and features in the order `features.json` does.
185 changes: 185 additions & 0 deletions runtime/conformance/spec/catalog/rust.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package catalog

import (
"list"
"regexp"
"strings"
)

// `rust/src/catalog.rs`: the catalog as Rust data, for the `comline-catalog`
// crate. `rust/src/lib.rs` defines the types it fills. Strings are raw
// literals, so no text may hold `"#`.

_rust: {
// A Rust raw string literal.
str: {
#in: string
out: "r#\"\(#in)\"#"
}
opt: {
#in: string | *null
let v = #in
out: [if v != null {"Some(\((str & {#in: v}).out))"}, "None"][0]
}
list: {
#in: [...string]
out: "&[" + strings.Join([for s in #in {(str & {#in: s}).out}], ", ") + "]"
}
// A constant's name: `packed@1` -> `PACKED_1`.
const: {
#in: string
out: strings.ToUpper(regexp.ReplaceAll("[^a-zA-Z0-9]+", #in, "_"))
}
kind: {
#in: "format" | "framing" | "handshake"
out: {format: "Kind::Format", framing: "Kind::Framing", handshake: "Kind::Handshake"}[#in]
}
status: {
#in: "stable" | "experimental" | "deprecated"
out: {stable: "Status::Stable", experimental: "Status::Experimental", deprecated: "Status::Deprecated"}[#in]
}
lifecycle: {
#in: {...}
let e = #in
out: """
status: \((status & {#in: e.status}).out),
replaced_by: \((opt & {#in: [if e.replaced_by != _|_ {e.replaced_by}, null][0]}).out),
since: \((opt & {#in: [if e.since != _|_ {e.since}, null][0]}).out),
"""
}
}

// No text may end a raw string early.
_raw_safe: [
for k, entries in {"guarantees": guarantees, "facts": facts, "encodings": encodings, "features": features}
for id, e in entries
for f, v in e
if (v & string) != _|_
if strings.Contains(v, "\"#") {"\(k).\(id).\(f)"},
] & []

// Constant names stay distinct within each kind.
_unique_consts: {
for k, entries in {"guarantees": guarantees, "facts": facts, "encodings": encodings, "features": features} {
(k): list.UniqueItems() & [for id, _ in entries {(_rust.const & {#in: id}).out}]
}
}

_ids: {
#in: {...}
out: strings.Join([for id, _ in #in {" pub const \((_rust.const & {#in: id}).out): &str = \((_rust.str & {#in: id}).out);"}], "\n")
}

_set: {
#name: string
#in: [...string]
let items = #in
out: " pub const \(#name): &[&str] = \((_rust.list & {#in: items}).out);"
}

rust_catalog: """
// Generated from the catalog's CUE by `specs/check.sh`: don't edit by hand.

use crate::{Encoding, Fact, Feature, Guarantee, Kind, Origin, Status, Value};

pub const SPEC_VERSION: &str = \((_rust.str & {#in: _spec_version}).out);

/// Every guarantee, in the catalog's order.
pub const GUARANTEES: &[Guarantee] = &[
\(strings.Join([for id, g in guarantees {"""
Guarantee {
id: \((_rust.str & {#in: id}).out),
title: \((_rust.str & {#in: g.title}).out),
description: \((_rust.str & {#in: g.description}).out),
comes_from: \({handshake: "Origin::Handshake", encoding: "Origin::Encoding", policy: "Origin::Policy", type: "Origin::Type"}[g.comes_from]),
\((_rust.lifecycle & {#in: g}).out)
},
"""}], "\n"))
];

/// Every fact, in the order profiles list them.
pub const FACTS: &[Fact] = &[
\(strings.Join([for id, f in facts {"""
Fact {
id: \((_rust.str & {#in: id}).out),
applies_to: \((_rust.kind & {#in: f.applies_to}).out),
title: \((_rust.str & {#in: f.title}).out),
description: \((_rust.str & {#in: f.description}).out),
values: &[\(strings.Join([for v, meaning in f.values {"Value { id: \((_rust.str & {#in: v}).out), meaning: \((_rust.str & {#in: meaning}).out) }"}], ", "))],
establishes: \((_rust.opt & {#in: [if f.establishes != _|_ {f.establishes}, null][0]}).out),
\((_rust.lifecycle & {#in: f}).out)
},
"""}], "\n"))
];

/// Every official encoding, by its wire name.
pub const ENCODINGS: &[Encoding] = &[
\(strings.Join([for id, e in encodings {"""
Encoding {
id: \((_rust.str & {#in: id}).out),
kind: \((_rust.kind & {#in: e.kind}).out),
title: \((_rust.str & {#in: e.title}).out),
description: \((_rust.str & {#in: e.description}).out),
aliases: \((_rust.list & {#in: e.aliases}).out),
frozen_as: \((_rust.opt & {#in: [if e.frozen_as != _|_ {e.frozen_as}, null][0]}).out),
profile: \(e.profile),
feature: \((_rust.opt & {#in: [if e.feature != _|_ {e.feature}, null][0]}).out),
tags: \((_rust.list & {#in: e.tags}).out),
\((_rust.lifecycle & {#in: e}).out)
},
"""}], "\n"))
];

/// Every wire feature, in the order `features.json` lists them.
pub const FEATURES: &[Feature] = &[
\(strings.Join([for id, f in features {"""
Feature {
id: \((_rust.str & {#in: id}).out),
title: \((_rust.str & {#in: f.title}).out),
description: \((_rust.str & {#in: f.description}).out),
\((_rust.lifecycle & {#in: f}).out)
},
"""}], "\n"))
];

/// Guarantee ids.
pub mod guarantee {
\((_ids & {#in: guarantees}).out)
}

/// Fact ids.
pub mod fact {
\((_ids & {#in: facts}).out)
}

/// Encoding wire names.
pub mod encoding {
\((_ids & {#in: encodings}).out)
}

/// Feature ids.
pub mod feature {
\((_ids & {#in: features}).out)
}

/// The catalog's sets.
pub mod sets {
\((_set & {#name: "GUARANTEES", #in: sets.guarantees}).out)
\((_set & {#name: "FORMATS", #in: sets.formats}).out)
\((_set & {#name: "FRAMINGS", #in: sets.framings}).out)
\((_set & {#name: "HANDSHAKES", #in: sets.handshakes}).out)
\((_set & {#name: "OFFICIAL", #in: sets.official}).out)
\((_set & {#name: "PROFILED", #in: sets.profiled}).out)
\((_set & {#name: "FORMAT_FACTS", #in: sets.format_facts}).out)
\((_set & {#name: "FRAMING_FACTS", #in: sets.framing_facts}).out)
\((_set & {#name: "HANDSHAKE_FACTS", #in: sets.handshake_facts}).out)
\((_set & {#name: "FEATURES", #in: sets.features}).out)
/// Every alias, with the wire name it resolves to.
pub const ALIASES: &[(&str, &str)] = &[\(strings.Join([for a, id in sets.aliases {"(\((_rust.str & {#in: a}).out), \((_rust.str & {#in: id}).out))"}], ", "))];
}

"""

// The spec's version, from `VERSION`: `specs/check.sh` passes it as
// `-t version=...`.
_spec_version: *"" | =~"^[0-9]+\\.[0-9]+$" @tag(version)
2 changes: 2 additions & 0 deletions runtime/conformance/spec/rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target/
Cargo.lock
13 changes: 13 additions & 0 deletions runtime/conformance/spec/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "comline-catalog"
version = "0.10.0"
edition = "2021"
rust-version = "1.82"
license = "MPL-2.0"
description = "The Comline specification's catalog of shared names, as Rust constants"
repository = "https://github.com/ComlineProject/specification"
publish = false

# No dependencies, and no_std: core's IR-only build, the runtime and the
# language server can all use it.
[dependencies]
Loading
Loading