Skip to content

Commit

Permalink
feat(go): implement the CLI for the Go code generator
Browse files Browse the repository at this point in the history
Although the Go code generator existed as library, it still needed the
CLI binary to drive the actual codegen step and write files to disk.
  • Loading branch information
dnaka91 committed Jan 9, 2024
1 parent 9f45b86 commit d3dc8d7
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 11 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/mabo-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/mabo-compiler/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("::")
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/mabo-go/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions crates/mabo-go/src/cli.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
#[arg(long)]
pub project_dir: Option<PathBuf>,
#[arg(long, short)]
pub out_dir: Option<PathBuf>,
#[arg(long)]
pub no_fmt: bool,
}

impl Cli {
Expand Down
4 changes: 2 additions & 2 deletions crates/mabo-go/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions crates/mabo-go/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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")
}
}
Expand Down
106 changes: 106 additions & 0 deletions crates/mabo-go/src/main.rs
Original file line number Diff line number Diff line change
@@ -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::<Result<Vec<_>>>()?;

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::<Result<Vec<_>>>()?;

let validated = validated
.iter()
.map(|(name, schema)| (*name, schema))
.collect::<Vec<_>>();

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(())
}

0 comments on commit d3dc8d7

Please sign in to comment.