Add source files: main, lib, cli, error, convert, highlight, validate, typescript
This commit is contained in:
203
src/main.rs
Normal file
203
src/main.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user