diff --git a/Cargo.lock b/Cargo.lock index b9492dd..7fc398e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -730,12 +730,15 @@ dependencies = [ name = "mabo-go" version = "0.1.0" dependencies = [ + "anyhow", "clap", "heck", "insta", "mabo-compiler", "mabo-parser", + "mabo-project", "miette", + "mimalloc", ] [[package]] diff --git a/crates/mabo-build/src/lib.rs b/crates/mabo-build/src/lib.rs index 3953ee5..6eb3d27 100644 --- a/crates/mabo-build/src/lib.rs +++ b/crates/mabo-build/src/lib.rs @@ -205,7 +205,7 @@ impl Compiler { } })?); - let out_file = out_dir.join(format!("{stem}.rs",)); + let out_file = out_dir.join(format!("{stem}.rs")); fs::write(&out_file, code).map_err(|source| Error::Write { source, diff --git a/crates/mabo-compiler/src/resolve/mod.rs b/crates/mabo-compiler/src/resolve/mod.rs index 01d7092..7725e29 100644 --- a/crates/mabo-compiler/src/resolve/mod.rs +++ b/crates/mabo-compiler/src/resolve/mod.rs @@ -89,7 +89,7 @@ pub(crate) struct Module<'a> { } impl Module<'_> { - fn path_to_string(&self)->String{ + fn path_to_string(&self) -> String { self.path.join("::") } } diff --git a/crates/mabo-go/Cargo.toml b/crates/mabo-go/Cargo.toml index bfa09df..e32caa5 100644 --- a/crates/mabo-go/Cargo.toml +++ b/crates/mabo-go/Cargo.toml @@ -15,6 +15,9 @@ heck = "0.4.1" miette = { workspace = true, features = ["fancy-no-backtrace"] } mabo-compiler = { path = "../mabo-compiler" } mabo-parser = { path = "../mabo-parser" } +mabo-project = { path = "../mabo-project" } +mimalloc.workspace = true +anyhow.workspace = true [dev-dependencies] insta.workspace = true diff --git a/crates/mabo-go/src/cli.rs b/crates/mabo-go/src/cli.rs index 7ec608f..a0ea8fa 100644 --- a/crates/mabo-go/src/cli.rs +++ b/crates/mabo-go/src/cli.rs @@ -1,10 +1,16 @@ +use std::path::PathBuf; + use clap::Parser; #[derive(Debug, Parser)] #[command(about, author, version, propagate_version = true)] pub struct Cli { - #[arg(num_args(1..))] - files: Vec, + #[arg(long)] + pub project_dir: Option, + #[arg(long, short)] + pub out_dir: Option, + #[arg(long)] + pub no_fmt: bool, } impl Cli { diff --git a/crates/mabo-go/src/decode.rs b/crates/mabo-go/src/decode.rs index cbe6e73..64c9fbe 100644 --- a/crates/mabo-go/src/decode.rs +++ b/crates/mabo-go/src/decode.rs @@ -113,10 +113,10 @@ impl Display for RenderFoundChecks<'_> { for field in &*self.0.fields { writeln!(f, "\tif !found{} {{", heck::AsUpperCamelCase(&field.name))?; writeln!(f, "\t\treturn nil, buf.MissingFieldError{{")?; - writeln!(f, "\t\t\tID: {}", field.id)?; + writeln!(f, "\t\t\tID: {},", field.id)?; writeln!( f, - "\t\t\tField: \"{}\"", + "\t\t\tField: \"{}\",", if self.0.kind == FieldKind::Named { &field.name } else { diff --git a/crates/mabo-go/src/definition.rs b/crates/mabo-go/src/definition.rs index e349826..6849a18 100644 --- a/crates/mabo-go/src/definition.rs +++ b/crates/mabo-go/src/definition.rs @@ -16,8 +16,8 @@ pub fn render_schema<'a>( let mut content = format!( "{}{}{}", RenderHeader, + RenderPackage(opts.package, None), RenderImports, - RenderPackage(opts.package, None) ); let modules = definitions @@ -36,9 +36,10 @@ fn render_definition<'a>(buf: &mut String, definition: &'a Definition<'_>) -> Op match definition { Definition::Module(m) => { let mut content = format!( - "{}{}", + "{}{}{}", RenderHeader, - RenderPackage(m.name, Some(&m.comment)) + RenderPackage(m.name, Some(&m.comment)), + RenderImports, ); let modules = m @@ -114,8 +115,8 @@ struct RenderImports; impl Display for RenderImports { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "import (")?; - writeln!(f, "\t\"github.com/dnaka91/mabo-go\"")?; - writeln!(f, "\t\"github.com/dnaka91/mabo-go/buf\"")?; + writeln!(f, "\tmabo \"github.com/dnaka91/mabo-go\"")?; + writeln!(f, "\tbuf \"github.com/dnaka91/mabo-go/buf\"")?; writeln!(f, ")\n") } } diff --git a/crates/mabo-go/src/main.rs b/crates/mabo-go/src/main.rs new file mode 100644 index 0000000..3db7339 --- /dev/null +++ b/crates/mabo-go/src/main.rs @@ -0,0 +1,106 @@ +//! TODO + +use std::{env, fs, path::Path, process::Command}; + +use anyhow::{bail, ensure, Context, Result}; +use mabo_go::{Opts, Output}; +use mabo_parser::Schema; + +use self::cli::Cli; + +mod cli; + +#[global_allocator] +static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; + +fn main() -> Result<()> { + let cli = Cli::parse(); + let project_dir = match cli.project_dir { + Some(dir) => dir, + None => env::current_dir().context("failed locating project directory")?, + }; + let out_dir = match cli.out_dir { + Some(dir) => dir, + None => env::current_dir().context("failed locating output directory")?, + }; + + ensure!( + project_dir.join("Mabo.toml").exists(), + "directory at {project_dir:?} doesn't appear to be a Mabo project" + ); + + let project = mabo_project::load(project_dir).context("failed loading project")?; + + fs::create_dir_all(&out_dir).context("failed creating output directory")?; + + let inputs = project + .files + .into_iter() + .map(|path| { + let input = fs::read_to_string(&path) + .with_context(|| format!("failed reading schema file at {path:?}"))?; + Ok((path, input)) + }) + .collect::>>()?; + + let validated = inputs + .iter() + .map(|(path, input)| { + let stem = path + .file_stem() + .context("missing file name")? + .to_str() + .context("invalid utf-8 encoding")?; + + let schema = Schema::parse(input, Some(path))?; + mabo_compiler::validate_schema(&schema)?; + + Ok((stem, schema)) + }) + .collect::>>()?; + + let validated = validated + .iter() + .map(|(name, schema)| (*name, schema)) + .collect::>(); + + mabo_compiler::resolve_schemas(&validated)?; + + let opts = Opts { + package: &project.project_file.package.name, + }; + + for (_, schema) in validated { + let schema = mabo_compiler::simplify_schema(schema); + let code = mabo_go::render_schema(&opts, &schema); + + write_output(code, &out_dir)?; + } + + if !cli.no_fmt { + let output = Command::new("gofmt") + .args(["-s", "-w"]) + .arg(out_dir) + .output()?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + bail!("failed to format output:\n{stderr}"); + } + } + + Ok(()) +} + +fn write_output(output: Output<'_>, parent: &Path) -> Result<()> { + let path = parent.join(output.name); + + fs::create_dir_all(&path)?; + fs::write(path.join(format!("{}.go", output.name)), output.content)?; + + for module in output.modules { + write_output(module, &path)?; + } + + Ok(()) +}