| Crates.io | jkconfig |
| lib.rs | jkconfig |
| version | 0.1.4 |
| created_at | 2025-10-24 04:51:42.390735+00 |
| updated_at | 2025-11-13 01:55:50.385085+00 |
| description | A Cursive-based TUI component library for JSON Schema configuration |
| homepage | |
| repository | https://github.com/ZR233/ostool |
| max_upload_size | |
| id | 1898003 |
| size | 274,610 |
A beautiful Terminal User Interface (TUI) configuration editor powered by JSON Schema. JKConfig automatically generates interactive forms from JSON Schema definitions, making configuration management intuitive and error-free.

Define your config in Rust → Generate JSON Schema → Edit with beautiful TUI
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Rust Structs │ │ JSON Schema │ │ Beautiful │
│ + schemars │ ──▶ │ (Auto-gen) │ ──▶ │ TUI Editor │
│ (#[derive]) │ │ config.schema │ │ (jkconfig) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
│ ▼
│ ┌─────────────────┐
└──────────────────────────────────────▶ │ Config File │
Load & Use │ .toml / .json │
└─────────────────┘
// ❌ Manual config editing - error prone
// Edit raw TOML/JSON → Easy to make mistakes → Runtime errors
// ✅ Type-safe from Rust to UI
// Define in Rust → Generate Schema → Edit with TUI → Load with confidence
Benefits:
Perfect for:
cargo install jkconfig
git clone https://github.com/ZR233/ostool.git
cd ostool/jkconfig
cargo build --release
The recommended workflow is to define your configuration structure in Rust and generate the JSON Schema automatically.
Add dependencies to your Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
schemars = { version = "1.0", features = ["derive"] }
Create your configuration structure:
use schemars::{JsonSchema, schema_for};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct AppConfig {
/// Server configuration
server: ServerConfig,
/// List of enabled features
features: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct ServerConfig {
/// Server hostname or IP address
host: String,
/// Server port number
port: u16,
/// Enable or disable the server
#[serde(default)]
enabled: bool,
}
fn main() {
// Generate JSON Schema
let schema = schema_for!(AppConfig);
let schema_json = serde_json::to_string_pretty(&schema).unwrap();
// Save to file
std::fs::write("config-schema.json", schema_json).unwrap();
println!("Schema generated successfully!");
}
Run to generate the schema:
cargo run
# Output: config-schema.json created
Create config.toml:
[server]
host = "localhost"
port = 8080
enabled = true
features = ["logging", "metrics", "auth"]
# Edit config.toml with the generated schema
jkconfig -c config.toml -s config-schema.json
# Or let it auto-detect (looks for config-schema.json)
jkconfig -c config.toml
Now you have a beautiful TUI to edit your configuration! 🎉
If you prefer writing JSON Schema directly:
Create config-schema.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "AppConfig",
"type": "object",
"properties": {
"server": {
"type": "object",
"description": "Server configuration",
"properties": {
"host": {
"type": "string",
"description": "Server hostname or IP address"
},
"port": {
"type": "integer",
"description": "Server port number"
},
"enabled": {
"type": "boolean",
"description": "Enable or disable the server"
}
},
"required": ["host", "port"]
},
"features": {
"type": "array",
"description": "List of enabled features",
"items": {
"type": "string"
}
}
},
"required": ["server", "features"]
}
jkconfig [OPTIONS]
Options:
-c, --config <FILE> Configuration file path [default: .project.toml]
-s, --schema <FILE> JSON Schema file path (auto-detected if not specified)
-h, --help Print help information
↑/↓ or j/k - Move cursor up/downEnter - Select/Edit itemEsc - Go back to previous menuC - Clear current valueM - Toggle menu state (for optional menus)Tab - Switch OneOf variantsS - Save and exitQ - Quit without saving~ - Toggle debug consoleEnter - Add new item or edit selected itemDel - Delete selected itemEsc - Close array editor| Type | Description | Example |
|---|---|---|
string |
Text input | "hostname" |
integer |
Whole numbers | 8080 |
number |
Decimal numbers | 3.14 |
boolean |
True/false toggle | true |
enum |
Selection list | ["option1", "option2"] |
array |
List of items | ["item1", "item2"] |
object |
Nested structure | {"key": "value"} |
JKConfig supports both TOML and JSON formats:
[server]
host = "localhost"
port = 8080
features = ["feature1", "feature2"]
{
"server": {
"host": "localhost",
"port": 8080
},
"features": ["feature1", "feature2"]
}
If you don't specify a schema file, JKConfig will automatically look for:
<config-name>-schema.jsonExample:
app.toml → Schema: app-schema.jsonconfig.json → Schema: config-schema.jsonJKConfig automatically creates backups before saving:
bk-<timestamp>.<ext>bk-1698765432.tomlJKConfig handles complex nested structures:
{
"type": "object",
"properties": {
"database": {
"oneOf": [
{
"type": "object",
"properties": {
"PostgreSQL": {
"type": "object",
"properties": {
"host": { "type": "string" },
"port": { "type": "integer" }
}
}
}
},
{
"type": "object",
"properties": {
"MySQL": {
"type": "object",
"properties": {
"host": { "type": "string" },
"port": { "type": "integer" }
}
}
}
}
]
}
}
}
Here's a complete example showing the recommended workflow from Rust structs to TUI editing:
// config_types.rs
use schemars::{JsonSchema, schema_for};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct MyAppConfig {
/// Application name
pub app_name: String,
/// Server settings
pub server: ServerConfig,
/// Database configuration
pub database: DatabaseConfig,
/// Optional features
#[serde(default)]
pub features: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
#[serde(default = "default_true")]
pub enabled: bool,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
}
fn default_true() -> bool { true }
fn main() -> anyhow::Result<()> {
// 1. Generate schema
let schema = schema_for!(MyAppConfig);
std::fs::write(
"myapp-schema.json",
serde_json::to_string_pretty(&schema)?
)?;
// 2. Create default config
let default_config = MyAppConfig {
app_name: "My Application".to_string(),
server: ServerConfig {
host: "0.0.0.0".to_string(),
port: 8080,
enabled: true,
},
database: DatabaseConfig {
url: "postgres://localhost/mydb".to_string(),
max_connections: 10,
},
features: vec!["auth".to_string(), "logging".to_string()],
};
// Save as TOML
let toml_str = toml::to_string_pretty(&default_config)?;
std::fs::write("myapp.toml", toml_str)?;
println!("✓ Schema generated: myapp-schema.json");
println!("✓ Default config created: myapp.toml");
println!("\nNow run: jkconfig -c myapp.toml");
Ok(())
}
use serde::Deserialize;
#[derive(Deserialize)]
struct MyAppConfig {
app_name: String,
server: ServerConfig,
// ... other fields
}
fn main() -> anyhow::Result<()> {
// Load the config file (edited with jkconfig)
let config_str = std::fs::read_to_string("myapp.toml")?;
let config: MyAppConfig = toml::from_str(&config_str)?;
println!("Starting {} on {}:{}",
config.app_name,
config.server.host,
config.server.port
);
// Use your config...
Ok(())
}
You can also use JKConfig programmatically in your Rust code:
use jkconfig::data::AppData;
// Load configuration programmatically
let app_data = AppData::new(
Some("config.toml"),
Some("config-schema.json")
)?;
// Access the configuration tree
println!("{:#?}", app_data.root);
// Get as JSON for further processing
let json_value = app_data.root.as_json();
JKConfig supports complex enum types (OneOf in JSON Schema):
use schemars::{JsonSchema, schema_for};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub enum DatabaseType {
PostgreSQL {
host: String,
port: u16,
database: String,
},
MySQL {
host: String,
port: u16,
database: String,
},
SQLite {
path: String,
},
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct Config {
pub database: DatabaseType,
}
fn main() {
let schema = schema_for!(Config);
std::fs::write(
"config-schema.json",
serde_json::to_string_pretty(&schema).unwrap()
).unwrap();
}
In the TUI, you can use Tab to switch between different database types!
See the tests/ directory for complete examples:
Use doc comments in your Rust structs - they become descriptions in the UI:
#[derive(JsonSchema, Serialize, Deserialize)]
struct Config {
/// The server hostname or IP address.
///
/// Examples: "localhost", "0.0.0.0", "example.com"
/// Default: "127.0.0.1"
host: String,
}
#[derive(JsonSchema, Serialize, Deserialize)]
struct Config {
#[serde(default = "default_port")]
port: u16,
}
fn default_port() -> u16 { 8080 }
#[derive(JsonSchema, Serialize, Deserialize)]
enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
// ✅ Good: Organized in modules
#[derive(JsonSchema, Serialize, Deserialize)]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
logging: LoggingConfig,
}
// ❌ Avoid: Flat structure with many fields
struct AppConfig {
server_host: String,
server_port: u16,
db_host: String,
db_port: u16,
// ... 50 more fields
}
#[derive(JsonSchema, Serialize, Deserialize)]
struct Config {
#[serde(default = "config_version")]
version: String,
// ... rest of config
}
fn config_version() -> String { "1.0.0".to_string() }
Add a build.rs:
// build.rs
use schemars::schema_for;
fn main() {
let schema = schema_for!(MyConfig);
std::fs::write(
"config-schema.json",
serde_json::to_string_pretty(&schema).unwrap()
).unwrap();
}
use clap::Parser;
#[derive(Parser)]
enum Cli {
/// Run the application
Run,
/// Edit configuration with TUI
Config {
#[arg(short, long, default_value = "config.toml")]
file: String,
},
}
fn main() {
match Cli::parse() {
Cli::Run => run_app(),
Cli::Config { file } => {
// Launch jkconfig
std::process::Command::new("jkconfig")
.arg("-c")
.arg(&file)
.status()
.expect("Failed to launch jkconfig");
}
}
}
Error: Schema file does not exist: config-schema.json
Solution: Ensure your schema file exists and the path is correct.
Error: Unsupported config file extension: "yaml"
Solution: Use .toml or .json extensions.
Error: Schema conversion error at path...
Solution: Validate your JSON Schema using JSON Schema Validator
| Feature | JKConfig | Other Config Editors |
|---|---|---|
| JSON Schema Support | ✅ Full | ⚠️ Limited |
| Terminal UI | ✅ Modern | ⚠️ Basic |
| Multiple Formats | ✅ TOML/JSON | ⚠️ Varies |
| Type Safety | ✅ Full | ❌ Manual |
| Auto Backup | ✅ Yes | ❌ No |
| Nested Structures | ✅ Full | ⚠️ Limited |
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)This project is licensed under either of:
at your option.
menuconfigMade with ❤️ by the ostool team