Add source files: main, lib, cli, error, convert, highlight, validate, typescript
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-01-29 15:15:41 +00:00
parent 174e874a96
commit aa96021b78

203
src/main.rs Normal file
View File

@@ -0,0 +1,203 @@
use std::process;
use config_forge::{
cli::{parse_args, Command, OutputFormat, SchemaSource},
convert::{convert, detect_format, parse_content, read_file, write_file, ConfigFormat, infer_schema},
highlight::{highlight_env, highlight_ini, highlight_value, highlight_yaml},
validate::validate_config,
typescript::{generate_ts_interface},
ConfigForgeError,
};
fn main() {
let args = parse_args();
if args.no_color {
colored::control::set_override(false);
}
if let Err(e) = run(args) {
eprintln!("Error: {}", e);
process::exit(1);
}
}
fn run(args: config_forge::cli::Args) -> std::result::Result<(), ConfigForgeError> {
match args.command {
Command::Convert(convert_args) => {
let content = read_file(&convert_args.input)?;
let format = convert_args.from.unwrap_or_else(|| detect_format(&convert_args.input.to_string_lossy()));
let value = parse_content(&content, format)?;
let converted = convert(value, convert_args.to.into())?;
if let Some(output) = convert_args.output {
write_file(&output, &converted)?;
if args.verbose {
println!("Converted {} to {}", convert_args.input.display(), output.display());
}
} else {
if !convert_args.no_highlight {
let highlighted = match convert_args.to {
OutputFormat::Json => {
let json_val: serde_json::Value = serde_json::from_str(&converted)
.unwrap_or_else(|_| serde_json::Value::String(converted.clone()));
highlight_value(&json_val, 0)
}
OutputFormat::Yaml => highlight_yaml(&converted),
OutputFormat::Ini => highlight_ini(&converted),
OutputFormat::Env => highlight_env(&converted),
OutputFormat::Toml => highlight_yaml(&converted),
};
println!("{}", highlighted);
} else {
println!("{}", converted);
}
}
}
Command::Validate(validate_args) => {
let config_content = read_file(&validate_args.config)?;
let format = detect_format(&validate_args.config.to_string_lossy());
let (schema_path, schema_format) = if let Some(schema_file) = validate_args.schema_file {
(schema_file.to_string_lossy().to_string(), "file")
} else if let Some(schema) = validate_args.schema {
(schema, "inline")
} else {
return Err(ConfigForgeError::ValidationError {
path: String::new(),
reason: "No schema provided. Use --schema or --schema-file".to_string(),
});
};
let result = validate_config(&config_content, &format.to_string(), &schema_path, schema_format)?;
if result.valid {
println!("{}", "Validation passed".green().bold());
} else {
println!("{}", "Validation failed".red().bold());
for error in result.errors {
eprintln!(" {}: {}", error.path, error.message);
}
process::exit(1);
}
}
Command::GenerateTs(generate_args) => {
let content = read_file(&generate_args.input)?;
let format = generate_args.from.unwrap_or_else(|| detect_format(&generate_args.input.to_string_lossy()));
let value = parse_content(&content, format)?;
let interface_name = generate_args.interface_name.unwrap_or_else(|| {
generate_args.input.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("Config")
.to_string()
});
let ts = generate_ts_interface(&value, &interface_name)?;
if let Some(output) = generate_args.output {
write_file(&output, &ts)?;
if args.verbose {
println!("Generated TypeScript interface {} to {}", interface_name, output.display());
}
} else {
println!("{}", ts);
}
}
Command::Batch(batch_args) => {
use rayon::prelude::*;
let pattern = batch_args.pattern;
let output_dir = batch_args.output_dir.unwrap_or_else(|| std::path::PathBuf::from("."));
let files: Vec<_> = glob::glob(&pattern)
.unwrap_or_else(|_| vec![])
.filter_map(|e| e.ok())
.filter(|p| p.is_file())
.collect();
if files.is_empty() {
println!("No files found matching pattern: {}", pattern);
return Ok(());
}
if args.verbose {
println!("Found {} files to convert", files.len());
}
let convert_file = |input_path: std::path::PathBuf| -> std::result::Result<(), ConfigForgeError> {
let content = read_file(&input_path)?;
let format = detect_format(&input_path.to_string_lossy());
let value = parse_content(&content, format)?;
let converted = convert(value, batch_args.to.into())?;
let file_name = input_path.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("output");
let output_path = output_dir.join(format!("{}.{}", file_name, batch_args.to));
write_file(&output_path, &converted)?;
Ok(())
};
if batch_args.parallel {
files.par_iter()
.try_for_each(|entry| convert_file(entry.clone()))?;
} else {
for entry in &files {
convert_file(entry.clone())?;
}
}
if args.verbose {
println!("Converted {} files to {}", files.len(), output_dir.display());
}
}
Command::Infer(infer_args) => {
let content = read_file(&infer_args.input)?;
let format = infer_args.from.unwrap_or_else(|| detect_format(&infer_args.input.to_string_lossy()));
let value = parse_content(&content, format)?;
let schema = infer_schema(&value);
let schema_str = serde_json::to_string_pretty(&schema)?;
if let Some(output) = infer_args.output {
write_file(&output, &schema_str)?;
if args.verbose {
println!("Inferred schema from {} to {}", infer_args.input.display(), output.display());
}
} else {
println!("{}", schema_str);
}
}
Command::Init(init_args) => {
let output = init_args.output.unwrap_or_else(|| std::path::PathBuf::from("configforge.toml"));
let default_config = r#"# ConfigForge default configuration
output_dir = "./"
default_format = "json"
color_output = true
parallel_processing = false
"#;
write_file(&output, default_config)?;
if args.verbose {
println!("Created default configuration at {}", output.display());
}
}
}
Ok(())
}
impl From<OutputFormat> for ConfigFormat {
fn from(format: OutputFormat) -> Self {
match format {
OutputFormat::Json => ConfigFormat::Json,
OutputFormat::Yaml => ConfigFormat::Yaml,
OutputFormat::Toml => ConfigFormat::Toml,
OutputFormat::Env => ConfigFormat::Env,
OutputFormat::Ini => ConfigFormat::Ini,
}
}
}