1
0
Fork 0
forked from wry/wry

refactor: split cargo workspace

This commit is contained in:
kossLAN 2026-06-05 11:56:21 -04:00
parent 5db14936e7
commit 1c21bd1259
695 changed files with 32023 additions and 44964 deletions

View file

@ -0,0 +1,12 @@
[package]
name = "toml-spec"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0.197", features = ["derive"] }
serde_yaml = "0.9.32"
anyhow = "1.0.81"
indexmap = { version = "2.2.5", features = ["serde"] }
error_reporter = "1.0.0"
serde_json = { version = "1.0.114", features = ["preserve_order"] }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
# Jay TOML Config
This document describes the format of the TOML configuration of the Jay compositor.
A JSON Schema for this format is available at [spec.generated.json](./spec.generated.json). You can include this file in your editor to get auto completion.
Start at the top-level type: [Config](#types-config).
## Types

View file

@ -0,0 +1,196 @@
use {
crate::types::{
ArraySpec, Described, MapSpec, NestableTypesSpec, NumberSpec, RefOrSpec, SingleTableSpec,
StringSpec, TableSpec, TopLevelTypeSpec, VariantSpec,
},
anyhow::Result,
serde_json::{Map, Value, json},
};
pub fn generate_json_schema(
types_sorted: &[(&String, &Described<TopLevelTypeSpec>)],
) -> Result<()> {
let mut types = Map::new();
for (name, ty) in types_sorted {
types.insert(name.to_string(), create_top_level_schema(ty));
}
let json = json!({
"$id": "urn:jay_toml_schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/Config",
"$defs": types,
});
let json = serde_json::to_string_pretty(&json).unwrap();
std::fs::write(
concat!(env!("CARGO_MANIFEST_DIR"), "/spec/spec.generated.json"),
json.as_bytes(),
)?;
Ok(())
}
fn create_top_level_schema(spec: &Described<TopLevelTypeSpec>) -> Value {
match &spec.value {
TopLevelTypeSpec::Variable { variants } => {
let mut cases = vec![];
for variant in variants {
cases.push(create_variant_schema(&variant.description, &variant.value));
}
json!({
"description": spec.description,
"anyOf": cases,
})
}
TopLevelTypeSpec::Single(variant) => create_variant_schema(&spec.description, variant),
}
}
fn create_variant_schema(description: &str, spec: &VariantSpec) -> Value {
macro_rules! spec {
($v:expr) => {
match $v {
RefOrSpec::Ref { name } => return create_ref_spec(description, name),
RefOrSpec::Spec(s) => s,
}
};
}
match spec {
VariantSpec::String(ss) => {
let ss = spec!(ss);
create_string_spec(description, ss)
}
VariantSpec::Number(ns) => {
let ns = spec!(ns);
create_number_spec(description, ns)
}
VariantSpec::Boolean => create_boolean_spec(description),
VariantSpec::Array(s) => {
let s = spec!(s);
create_array_spec(description, s)
}
VariantSpec::Table(ts) => {
let ts = spec!(ts);
match ts {
TableSpec::Tagged { types } => {
let mut variants = vec![];
for (name, ty) in types {
variants.push(create_single_table_spec(
&ty.description,
&ty.value,
Some(name),
));
}
json!({
"description": description,
"anyOf": variants,
})
}
TableSpec::Single(s) => create_single_table_spec(description, s, None),
}
}
}
}
fn create_single_table_spec(
description: &str,
spec: &SingleTableSpec,
type_: Option<&str>,
) -> Value {
let mut properties = Map::new();
let mut required = vec![];
if let Some(type_) = type_ {
properties.insert("type".into(), json!({ "const": type_ }));
required.push("type".into());
}
for (key, val) in &spec.fields {
properties.insert(
key.into(),
create_nestable_type_spec(&val.description, &val.value.kind),
);
if val.value.required {
required.push(key.to_string());
}
}
json!({
"description": description,
"type": "object",
"properties": properties,
"required": required,
})
}
fn create_ref_spec(description: &str, name: &str) -> Value {
let path = format!("#/$defs/{name}");
json!({
"description": description,
"$ref": path,
})
}
fn create_nestable_type_spec(description: &str, spec: &RefOrSpec<NestableTypesSpec>) -> Value {
let spec = match spec {
RefOrSpec::Ref { name } => return create_ref_spec(description, name),
RefOrSpec::Spec(s) => s,
};
match spec {
NestableTypesSpec::String(s) => create_string_spec(description, s),
NestableTypesSpec::Number(s) => create_number_spec(description, s),
NestableTypesSpec::Boolean => create_boolean_spec(description),
NestableTypesSpec::Array(s) => create_array_spec(description, s),
NestableTypesSpec::Map(s) => create_map_spec(description, s),
}
}
fn create_map_spec(description: &str, spec: &MapSpec) -> Value {
json!({
"description": description,
"type": "object",
"additionalProperties": create_nestable_type_spec("", &spec.values),
})
}
fn create_string_spec(description: &str, spec: &StringSpec) -> Value {
let mut res = Map::new();
res.insert("type".into(), json!("string"));
res.insert("description".into(), json!(description));
if let Some(values) = &spec.values {
let strings: Vec<_> = values.iter().map(|v| &v.value.value).collect();
res.insert("enum".into(), json!(strings));
} else if let Some(pattern) = &spec.pattern {
res.insert("pattern".into(), json!(pattern));
}
res.into()
}
fn create_array_spec(description: &str, spec: &ArraySpec) -> Value {
json!({
"type": "array",
"description": description,
"items": create_nestable_type_spec("", &spec.items),
})
}
fn create_number_spec(description: &str, spec: &NumberSpec) -> Value {
let ty = match spec.integer_only {
true => "integer",
false => "number",
};
let mut res = Map::new();
res.insert("type".into(), json!(ty));
res.insert("description".into(), json!(description));
if let Some(minimum) = spec.minimum {
let key = match spec.exclusive_minimum {
true => "exclusiveMinimum",
false => "minimum",
};
res.insert(key.into(), json!(minimum));
}
res.into()
}
fn create_boolean_spec(description: &str) -> Value {
json!({"type": "boolean", "description": description})
}

View file

@ -0,0 +1,27 @@
use {
crate::{
json_schema::generate_json_schema,
markdown::generate_markdown,
types::{Described, TopLevelTypeSpec},
},
anyhow::Result,
indexmap::IndexMap,
};
mod json_schema;
mod markdown;
mod types;
fn parse() -> Result<IndexMap<String, Described<TopLevelTypeSpec>>> {
let file = std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/spec/spec.yaml"))?;
Ok(serde_yaml::from_str(&file)?)
}
fn main() -> Result<()> {
let types = parse()?;
let mut types_sorted: Vec<_> = types.iter().collect();
types_sorted.sort_by_key(|t| t.0);
generate_markdown(&types_sorted)?;
generate_json_schema(&types_sorted)?;
Ok(())
}

View file

@ -0,0 +1,268 @@
use {
crate::types::{
ArraySpec, Described, NestableTypesSpec, NumberSpec, RefOrSpec, SingleTableSpec,
StringSpec, TableSpec, TopLevelTypeSpec, VariantSpec,
},
anyhow::Result,
std::io::Write,
};
pub fn generate_markdown(types: &[(&String, &Described<TopLevelTypeSpec>)]) -> Result<()> {
const TEMPLATE: &str = include_str!("../spec/template.md");
let mut buf = vec![];
buf.extend_from_slice(TEMPLATE.as_bytes());
for (name, ty) in types {
write_top_level_type_spec(&mut buf, name, ty)?;
}
std::fs::write(
concat!(env!("CARGO_MANIFEST_DIR"), "/spec/spec.generated.md"),
&buf,
)?;
Ok(())
}
fn write_top_level_type_spec(
buf: &mut Vec<u8>,
name: &str,
spec: &Described<TopLevelTypeSpec>,
) -> Result<()> {
writeln!(buf, "<a name=\"types-{name}\"></a>")?;
writeln!(buf, "### `{name}`")?;
writeln!(buf)?;
writeln!(buf, "{}", spec.description.trim())?;
writeln!(buf)?;
match &spec.value {
TopLevelTypeSpec::Variable { variants } => {
writeln!(
buf,
"Values of this type should have one of the following forms:"
)?;
writeln!(buf)?;
for variant in variants {
write!(buf, "#### ")?;
let name = match &variant.value {
VariantSpec::String(_) => "A string",
VariantSpec::Number(_) => "A number",
VariantSpec::Boolean => "A boolean",
VariantSpec::Array(_) => "An array",
VariantSpec::Table(_) => "A table",
};
writeln!(buf, "{name}")?;
writeln!(buf)?;
writeln!(buf, "{}", variant.description.trim())?;
writeln!(buf)?;
write_variant_spec(buf, &variant.value)?;
}
}
TopLevelTypeSpec::Single(variant) => {
let name = match &variant {
VariantSpec::String(_) => "strings",
VariantSpec::Number(_) => "numbers",
VariantSpec::Boolean => "booleans",
VariantSpec::Array(_) => "arrays",
VariantSpec::Table(_) => "tables",
};
writeln!(buf, "Values of this type should be {name}.")?;
writeln!(buf)?;
write_variant_spec(buf, variant)?;
}
}
writeln!(buf)?;
Ok(())
}
fn write_variant_spec(buf: &mut Vec<u8>, spec: &VariantSpec) -> Result<()> {
macro_rules! spec {
($v:expr) => {
match $v {
RefOrSpec::Ref { name } => {
writeln!(buf, "The value should be a [{name}](#types-{name}).")?;
writeln!(buf)?;
return Ok(());
}
RefOrSpec::Spec(s) => s,
}
};
}
match spec {
VariantSpec::String(ss) => {
let ss = spec!(ss);
write_string_spec(buf, ss, "")?;
}
VariantSpec::Number(ns) => {
let ns = spec!(ns);
write_number_spec(buf, ns, "")?;
}
VariantSpec::Boolean => {}
VariantSpec::Array(s) => {
let s = spec!(s);
write_array_spec(buf, s, "")?;
}
VariantSpec::Table(ts) => {
let ts = spec!(ts);
match ts {
TableSpec::Tagged { types } => {
writeln!(
buf,
"This table is a tagged union. The variant is determined by the `type` field. It takes one of the following values:"
)?;
writeln!(buf)?;
for (name, spec) in types {
writeln!(buf, "- `{name}`:")?;
writeln!(buf)?;
for line in spec.description.trim().lines() {
writeln!(buf, " {line}")?;
}
writeln!(buf)?;
write_single_table_spec(buf, &spec.value, " ")?;
}
}
TableSpec::Single(s) => {
write_single_table_spec(buf, s, "")?;
}
}
}
}
Ok(())
}
fn write_single_table_spec(buf: &mut Vec<u8>, spec: &SingleTableSpec, pad: &str) -> Result<()> {
writeln!(buf, "{pad}The table has the following fields:")?;
writeln!(buf)?;
for (name, fs) in &spec.fields {
let optional = match fs.value.required {
true => "required",
false => "optional",
};
writeln!(buf, "{pad}- `{name}` ({optional}):")?;
writeln!(buf)?;
for line in fs.description.trim().lines() {
writeln!(buf, "{pad} {line}")?;
}
writeln!(buf)?;
write!(buf, "{pad} The value of this field should be ")?;
let spec = write_nestable_type_spec(buf, &fs.value.kind, false)?;
writeln!(buf, ".")?;
writeln!(buf)?;
if let Some(spec) = spec {
let pad = format!("{pad} ");
write_nestable_type_restrictions(buf, spec, &pad)?;
}
}
Ok(())
}
fn write_nestable_type_spec<'a>(
buf: &mut Vec<u8>,
spec: &'a RefOrSpec<NestableTypesSpec>,
plural: bool,
) -> Result<Option<&'a NestableTypesSpec>> {
let spec = match spec {
RefOrSpec::Ref { name } => {
if plural {
write!(buf, "[{name}s](#types-{name})")?;
} else {
write!(buf, "a [{name}](#types-{name})")?;
}
return Ok(None);
}
RefOrSpec::Spec(s) => s,
};
let name = match (spec, plural) {
(NestableTypesSpec::String(_), false) => "a string",
(NestableTypesSpec::String(_), true) => "strings",
(NestableTypesSpec::Number(_), false) => "a number",
(NestableTypesSpec::Number(_), true) => "numbers",
(NestableTypesSpec::Boolean, false) => "a boolean",
(NestableTypesSpec::Boolean, true) => "booleans",
(NestableTypesSpec::Map(s), _) => {
let name = match plural {
true => "tables",
false => "a table",
};
write!(buf, "{name} whose values are ")?;
return write_nestable_type_spec(buf, &s.values, true);
}
(NestableTypesSpec::Array(s), _) => {
let name = match plural {
true => "arrays",
false => "an array",
};
write!(buf, "{name} of ")?;
return write_nestable_type_spec(buf, &s.items, true);
}
};
write!(buf, "{name}")?;
Ok(Some(spec))
}
fn write_nestable_type_restrictions(
buf: &mut Vec<u8>,
spec: &NestableTypesSpec,
pad: &str,
) -> Result<()> {
match spec {
NestableTypesSpec::String(s) => write_string_spec(buf, s, pad),
NestableTypesSpec::Number(s) => write_number_spec(buf, s, pad),
NestableTypesSpec::Boolean => Ok(()),
NestableTypesSpec::Array(_) => Ok(()),
NestableTypesSpec::Map(_) => Ok(()),
}
}
fn write_string_spec(buf: &mut Vec<u8>, spec: &StringSpec, pad: &str) -> Result<()> {
if let Some(values) = &spec.values {
writeln!(
buf,
"{pad}The string should have one of the following values:"
)?;
writeln!(buf)?;
for value in values {
writeln!(buf, "{pad}- `{}`:", value.value.value)?;
writeln!(buf)?;
for line in value.description.lines() {
writeln!(buf, "{pad} {line}")?;
}
writeln!(buf)?;
}
writeln!(buf)?;
} else if let Some(pattern) = &spec.pattern {
writeln!(
buf,
"{pad}The string should match the following regular expression: `{pattern}`"
)?;
writeln!(buf)?;
}
Ok(())
}
fn write_array_spec(buf: &mut Vec<u8>, spec: &ArraySpec, pad: &str) -> Result<()> {
write!(buf, "{pad}Each element of this array should be ")?;
let spec = write_nestable_type_spec(buf, &spec.items, false)?;
writeln!(buf, ".")?;
writeln!(buf)?;
if let Some(spec) = spec {
write_nestable_type_restrictions(buf, spec, pad)?;
}
Ok(())
}
fn write_number_spec(buf: &mut Vec<u8>, spec: &NumberSpec, pad: &str) -> Result<()> {
if spec.integer_only {
writeln!(buf, "{pad}The numbers should be integers.")?;
writeln!(buf)?;
}
if let Some(minimum) = spec.minimum {
let greater = match spec.exclusive_minimum {
true => "strictly greater than",
false => "greater than or equal to",
};
writeln!(buf, "{pad}The numbers should be {greater} {minimum}.")?;
writeln!(buf)?;
}
Ok(())
}

View file

@ -0,0 +1,185 @@
use {
error_reporter::Report,
indexmap::IndexMap,
serde::{
Deserialize, Deserializer,
de::{DeserializeOwned, Error},
},
};
#[derive(Debug, Deserialize)]
pub struct Described<T> {
pub description: String,
#[serde(flatten)]
pub value: T,
}
#[derive(Debug)]
pub enum TopLevelTypeSpec {
Variable {
variants: Vec<Described<VariantSpec>>,
},
Single(VariantSpec),
}
#[derive(Debug)]
pub enum TableSpec {
Tagged {
types: IndexMap<String, Described<SingleTableSpec>>,
},
Single(SingleTableSpec),
}
#[derive(Debug, Deserialize)]
pub struct SingleTableSpec {
pub fields: IndexMap<String, Described<TableFieldSpec>>,
}
#[derive(Debug, Deserialize)]
pub struct TableFieldSpec {
pub required: bool,
#[serde(flatten)]
pub kind: RefOrSpec<NestableTypesSpec>,
}
#[derive(Debug)]
pub enum RefOrSpec<T> {
Ref { name: String },
Spec(T),
}
#[derive(Debug, Deserialize)]
pub struct StringSpec {
pub pattern: Option<String>,
pub values: Option<Vec<Described<StringSpecValue>>>,
}
#[derive(Debug, Deserialize)]
pub struct StringSpecValue {
pub value: String,
}
#[derive(Debug, Deserialize)]
pub struct NumberSpec {
#[serde(default)]
pub integer_only: bool,
pub minimum: Option<f64>,
#[serde(default)]
pub exclusive_minimum: bool,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "kind")]
pub enum VariantSpec {
String(RefOrSpec<StringSpec>),
Number(RefOrSpec<NumberSpec>),
Boolean,
Array(RefOrSpec<ArraySpec>),
Table(RefOrSpec<TableSpec>),
}
#[derive(Debug, Deserialize)]
pub struct ArraySpec {
pub items: Box<RefOrSpec<NestableTypesSpec>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "kind")]
pub enum NestableTypesSpec {
String(StringSpec),
Number(NumberSpec),
Boolean,
Array(ArraySpec),
Map(MapSpec),
}
#[derive(Debug, Deserialize)]
pub struct MapSpec {
pub values: Box<RefOrSpec<NestableTypesSpec>>,
}
impl<'de> Deserialize<'de> for TopLevelTypeSpec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let v = serde_yaml::Value::deserialize(deserializer)?;
#[derive(Debug, Deserialize)]
struct Variable {
variants: Vec<Described<VariantSpec>>,
}
let variable = Variable::deserialize(&v);
let single = VariantSpec::deserialize(&v);
let res = match (variable, single) {
(Ok(variable), _) => Self::Variable {
variants: variable.variants,
},
(_, Ok(single)) => Self::Single(single),
(Err(e1), Err(e2)) => {
return Err(Error::custom(format!(
"spec must define either variants or a single variant. failures: {} ----- {}",
Report::new(e1),
Report::new(e2)
)));
}
};
Ok(res)
}
}
impl<'de> Deserialize<'de> for TableSpec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let v = serde_yaml::Value::deserialize(deserializer)?;
#[derive(Debug, Deserialize)]
struct Tagged {
types: IndexMap<String, Described<SingleTableSpec>>,
}
let tagged = Tagged::deserialize(&v);
let single = SingleTableSpec::deserialize(&v);
let res = match (tagged, single) {
(Ok(tagged), _) => Self::Tagged {
types: tagged.types,
},
(_, Ok(single)) => Self::Single(single),
(Err(e1), Err(e2)) => {
return Err(Error::custom(format!(
"spec must define either types or fields. failures: {} ----- {}",
Report::new(e1),
Report::new(e2)
)));
}
};
Ok(res)
}
}
impl<'de, U: DeserializeOwned> Deserialize<'de> for RefOrSpec<U> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let v = serde_yaml::Value::deserialize(deserializer)?;
#[derive(Debug, Deserialize)]
struct Ref {
#[serde(rename = "ref")]
name: String,
}
let name = Ref::deserialize(&v);
let single = U::deserialize(&v);
let res = match (name, single) {
(Ok(name), _) => Self::Ref { name: name.name },
(_, Ok(single)) => Self::Spec(single),
(Err(e1), Err(e2)) => {
return Err(Error::custom(format!(
"spec must define either a ref or a spec. failures: {} ----- {}",
Report::new(e1),
Report::new(e2)
)));
}
};
Ok(res)
}
}