Skip to content

Commit

Permalink
feat: codegen settings
Browse files Browse the repository at this point in the history
Fixes #2633
  • Loading branch information
jdx committed Sep 24, 2024
1 parent a8f7493 commit 19e7027
Show file tree
Hide file tree
Showing 14 changed files with 1,136 additions and 491 deletions.
1 change: 1 addition & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jq = "latest"
"npm:prettier" = "3"
direnv = "latest"
actionlint = "latest"
"pipx:toml-sort" = "latest"
#python = { version = "latest", virtualenv = "{{env.HOME}}/.cache/venv" }
#ruby = "3.1"

Expand Down
1 change: 1 addition & 0 deletions .mise/tasks/lint-fix
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ shfmt -w -i 2 -ci -bn "${scripts_dirs[@]}"
prettier -w $(git ls-files '*.yml' '*.yaml')
markdownlint --fix .
actionlint
toml-sort -i settings.toml

cat >rustfmt.toml <<EOF
unstable_features = true
Expand Down
4 changes: 4 additions & 0 deletions .mise/tasks/lint/settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail

toml-sort --check settings.toml
37 changes: 37 additions & 0 deletions .mise/tasks/render/settings
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env node

const fs = require('fs');
const toml = require('toml');
const child_process = require('child_process');
const Handlebars = require('handlebars');

const doc = toml.parse(fs.readFileSync('settings.toml', 'utf-8'));
const settings = {};

for (const key in doc) {
const props = doc[key];
if (props.type) {
let type = props.type;
if (type === 'bool') {
type = 'boolean';
}
if (!props.description) {
console.error(`Missing description for ${key}`);
process.exit(1);
}
settings[key] = {
type,
default: props.default,
description: props.description,
parse_env: props.parse_env
};
}
}

const schema_tmpl = Handlebars.compile(fs.readFileSync('schema/mise.json.hbs', 'utf-8'));
fs.writeFileSync('schema/mise.json.tmp', schema_tmpl({
settings_json: new Handlebars.SafeString(JSON.stringify(settings, null, 2)),
}));

child_process.execSync('jq . < schema/mise.json.tmp > schema/mise.json');
fs.unlinkSync('schema/mise.json.tmp');
18 changes: 11 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ license = "MIT"
keywords = ["mise"]
categories = ["command-line-utilities"]
include = [
"src/**/*.rs",
"src/plugins/core/assets/**",
"src/assets/**",
"/completions/*",
"/Cargo.lock",
"/LICENSE",
"/README.md",
"/build.rs",
"/completions/*",
"/settings.toml",
"/zipsign.pub",
"src/**/*.rs",
"src/assets/**",
"src/plugins/core/assets/**",
]
rust-version = "1.76.0"
build = "build.rs"
Expand Down Expand Up @@ -69,7 +70,7 @@ heck = "0.5"
home = "0.5.9"
humantime = "2"
indenter = "0.3.3"
indexmap = { version = "2.2.6", features = ["serde"] }
indexmap = { version = "2", features = ["serde"] }
indicatif = { version = "0.17.8", features = ["default", "improved_unicode"] }
indoc = "2.0.5"
itertools = "0.13"
Expand Down Expand Up @@ -111,8 +112,8 @@ tokio = { version = "1.37.0", features = [
"rt",
"time",
] }
toml = { version = "0.8.12", features = ["parse"] }
toml_edit = { version = "0.22.12", features = ["parse"] }
toml = { version = "0.8", features = ["parse"] }
toml_edit = { version = "0.22", features = ["parse"] }
url = "2.5.0"
usage-lib = { version = "0.3", features = ["clap"] }
versions = { version = "6.2.0", features = ["serde"] }
Expand Down Expand Up @@ -141,6 +142,9 @@ sevenz-rust = "0.6"
[build-dependencies]
built = { version = "0.7", features = ["chrono", "git2"] }
cfg_aliases = "0.2"
heck = "0.5"
toml = "0.8"
indexmap = "2"

[dev-dependencies]
assert_cmd = "2.0.14"
Expand Down
89 changes: 89 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,96 @@
use heck::ToUpperCamelCase;
use indexmap::IndexMap;
use std::path::Path;
use std::{env, fs};

fn main() {
cfg_aliases::cfg_aliases! {
vfox: { any(feature = "vfox", target_os = "windows") },
asdf: { any(feature = "asdf", not(target_os = "windows")) },
}
built::write_built_file().expect("Failed to acquire build-time information");

codegen_settings();
}

fn codegen_settings() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("settings.rs");
let mut lines = vec![r#"
#[derive(Config, Default, Debug, Clone, Serialize)]
#[config(partial_attr(derive(Clone, Serialize, Default)))]
pub struct Settings {"#
.to_string()];

let settings: toml::Table = fs::read_to_string("settings.toml")
.unwrap()
.parse()
.unwrap();
let props_to_code = |key: &str, props: &toml::Value| {
let mut lines = vec![];
let props = props.as_table().unwrap();
if let Some(description) = props.get("description") {
lines.push(format!(" /// {}", description.as_str().unwrap()));
}
if let Some(type_) = props.get("type") {
let mut opts = IndexMap::new();
if let Some(env) = props.get("env") {
opts.insert("env".to_string(), env.to_string());
}
if let Some(default) = props.get("default") {
opts.insert("default".to_string(), default.to_string());
} else if type_.as_str().unwrap() == "bool" {
opts.insert("default".to_string(), "false".to_string());
}
if let Some(parse_env) = props.get("parse_env") {
opts.insert(
"parse_env".to_string(),
parse_env.as_str().unwrap().to_string(),
);
}
dbg!(&opts);
lines.push(format!(
" #[config({})]",
opts.iter()
.map(|(k, v)| format!("{k} = {v}"))
.collect::<Vec<_>>()
.join(", ")
));
lines.push(format!(" pub {}: {},", key, type_.as_str().unwrap()));
} else {
lines.push(" #[config(nested)]".to_string());
lines.push(format!(
" pub {}: Settings{},",
key,
key.to_upper_camel_case()
));
}
lines.join("\n")
};
for (key, props) in &settings {
lines.push(props_to_code(key, props));
}
lines.push("}".to_string());

let nested_settings = settings
.iter()
.filter(|(_, v)| !v.as_table().unwrap().contains_key("type"))
.collect::<Vec<_>>();
for (child, props) in nested_settings {
lines.push(format!(
r#"#[derive(Config, Default, Debug, Clone, Serialize)]
#[config(partial_attr(derive(Clone, Serialize, Default)))]
#[config(partial_attr(serde(deny_unknown_fields)))]
pub struct Settings{name} {{
"#,
name = child.to_upper_camel_case()
));

for (key, props) in props.as_table().unwrap() {
lines.push(props_to_code(key, props));
}
lines.push("}".to_string());
}

fs::write(&dest_path, lines.join("\n")).unwrap();
}
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "mise",
"version": "1.0.0",
"description": "<div align=\"center\"> <h1><a href=\"https://mise.jdx.dev\"> <img src=\"https://github.com/jdx/mise/assets/216188/27a8ea18-9383-4d86-a445-305b9a6248c1\" alt=\"mise-logo\" width=\"400\" /><br /> mise-en-place </a></h1> <!-- <a href=\"https://mise.jdx.dev\"><picture> --> <!-- <source media=\"(prefers-color-scheme: dark)\" width=\"617\" srcset=\"./docs/logo-dark@2x.png\"> --> <!-- <img alt=\"mise logo\" width=\"617\" src=\"./docs/logo-light@2x.png\"> --> <!-- </picture></a> --> <a href=\"https://crates.io/crates/mise\"><img alt=\"Crates.io\" src=\"https://img.shields.io/crates/v/mise?style=for-the-badge\"></a> <a href=\"https://github.com/jdx/mise/blob/main/LICENSE\"><img alt=\"GitHub\" src=\"https://img.shields.io/github/license/jdx/mise?color=%2344CC11&style=for-the-badge\"></a> <a href=\"https://github.com/jdx/mise/actions/workflows/test.yml\"><img alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/actions/workflow/status/jdx/mise/test.yml?style=for-the-badge\"></a> <a href=\"https://app.codacy.com/gh/jdx/mise/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage\"><img alt=\"Codacy coverage (branch)\" src=\"https://img.shields.io/codacy/coverage/af322e1f36ca41f0a296f49733a705f5/main?color=%2344CC11&style=for-the-badge\"></a> <a href=\"https://discord.gg/mABnUDvP57\"><img alt=\"Discord\" src=\"https://img.shields.io/discord/1066429325269794907?color=%23738ADB&style=for-the-badge\"></a> <p><em>The front-end to your dev env.</em></p> </div>",
"main": "index.js",
"directories": {
"doc": "docs",
"man": "man",
"test": "tests"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"handlebars": "^4.7.8",
"toml": "^3.0.0"
}
}
Loading

0 comments on commit 19e7027

Please sign in to comment.