Skip to content

Commit

Permalink
feat: add config file
Browse files Browse the repository at this point in the history
  • Loading branch information
threadexio committed Dec 31, 2024
1 parent efea7ac commit 21b24e9
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 77 deletions.
74 changes: 74 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ log = { version = "0.4" }
owo-colors = { version = "4.1" }
petgraph = { version = "0.6" }
rand = "0.8.5"
serde = { version = "1.0", features = ["derive"] }
thiserror = { version = "2.0" }
toml = { version = "0.8" }

[build-dependencies]
chrono = "0.4.39"
Expand Down
3 changes: 3 additions & 0 deletions cbundl.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
no-format = true
formatter = "/home/kat/.nix-profile/bin/clang-format"
deterministic = true
95 changes: 18 additions & 77 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,121 +1,62 @@
use std::fs::File;
use std::io::{stdout, Write};
use std::path::{Path, PathBuf};
use std::path::PathBuf;

use clap::Parser;
use eyre::{Context, Result};

use crate::banner::Banner;
use crate::bundler::Bundler;
use crate::consts::{CRATE_DESCRIPTION, LONG_VERSION, SHORT_VERSION};
use crate::config::Config;
use crate::display::display_path;
use crate::formatter::Formatter;
use crate::pipeline::Pipeline;
use crate::quotes::Quotes;
use crate::source::Sources;

#[derive(Debug, Clone, Parser)]
#[command(
version = SHORT_VERSION,
long_version = LONG_VERSION,
about = CRATE_DESCRIPTION,
long_about = None
)]
pub struct GlobalArgs {
#[arg(
long,
help = "Don't pass the resulting bundle through the formatter.",
default_value_t = false
)]
pub no_format: bool,

#[arg(
long,
help = "Code formatter executable.",
long_help = "Code formatter. Must format the code from stdin and write it to stdout.",
default_value = "clang-format",
value_name = "exe"
)]
pub formatter: PathBuf,

#[arg(
long = "deterministic",
help = "Output a deterministic bundle.",
default_value_t = false
)]
pub deterministic: bool,

#[arg(
short = 'o',
long = "output",
help = "Specify where to write the resulting bundle.",
default_value = "-",
value_name = "path"
)]
pub output_file: PathBuf,

#[arg(help = "Path to the entry source file.", value_name = "path")]
pub entry: PathBuf,
}

pub fn run() -> Result<()> {
let args = GlobalArgs::parse();
trace!("args = {args:#?}");
let config = Config::new()?;
trace!("config = {config:#?}");

let sources = Sources::new(args.entry)?;
let sources = Sources::new(config.entry)?;

let mut pipeline = Pipeline {
bundler: Bundler {},
banner: Banner {
quotes: Quotes {},
deterministic: args.deterministic,
deterministic: config.deterministic,
},
formatter: some_if(!args.no_format, || Formatter {
exe: args.formatter,
formatter: (!config.no_format).then(|| Formatter {
exe: config.formatter,
}),
};

let bundle = pipeline.process(&sources)?;

write_bundle(bundle, &args.output_file).with_context(|| {
format!(
"failed to write bundle to `{}`",
display_path(&args.output_file)
)
write_bundle(bundle, config.output_file.as_ref()).with_context(|| {
if let Some(path) = config.output_file.as_ref() {
format!("failed to write bundle to `{}`", display_path(path))
} else {
format!("failed to write bundle to stdout")
}
})?;

Ok(())
}

fn write_bundle(bundle: String, path: &Path) -> Result<()> {
let mut writer: Box<dyn Write> = if path_is_stdio(path) {
Box::new(stdout().lock())
} else {
fn write_bundle(bundle: String, path: Option<&PathBuf>) -> Result<()> {
let mut writer: Box<dyn Write> = if let Some(path) = path {
Box::new(
File::options()
.write(true)
.truncate(true)
.create(true)
.open(path)?,
)
} else {
Box::new(stdout().lock())
};

writer.write_all(bundle.as_bytes())?;
writer.flush()?;
Ok(())
}

fn path_is_stdio(path: &Path) -> bool {
path.as_os_str().as_encoded_bytes().eq(b"-")
}

fn some_if<T, F>(cond: bool, f: F) -> Option<T>
where
F: FnOnce() -> T,
{
if cond {
Some(f())
} else {
None
}
}
80 changes: 80 additions & 0 deletions src/config/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use std::path::{Path, PathBuf};

use clap::parser::ValueSource;
use clap::{CommandFactory, Parser};
use eyre::Result;

use crate::consts::{CRATE_DESCRIPTION, LONG_VERSION, SHORT_VERSION};

use super::Config;

#[derive(Debug, Clone, Parser)]
#[command(
version = SHORT_VERSION,
long_version = LONG_VERSION,
about = CRATE_DESCRIPTION,
long_about = None
)]
pub struct Args {
#[arg(long, help = "Don't pass the resulting bundle through the formatter.")]
no_format: bool,

#[arg(
long,
help = "Code formatter executable.",
long_help = "Code formatter. Must format the code from stdin and write it to stdout.",
value_name = "exe",
default_value = "clang-format"
)]
formatter: PathBuf,

#[arg(long = "deterministic", help = "Output a deterministic bundle.")]
deterministic: bool,

#[arg(
short = 'o',
long = "output",
help = "Specify where to write the resulting bundle.",
value_name = "path",
default_value = "-"
)]
output_file: PathBuf,

#[arg(help = "Path to the entry source file.", value_name = "path")]
entry: PathBuf,
}

impl Args {
pub fn merge(config: &mut Config) -> Result<()> {
let matches = Args::command().get_matches();

// SAFETY: The following `unwrap`s are safe because the these arguments have default values.

if matches.contains_id("no_format") {
config.no_format = matches.get_flag("no_format");
}

if matches.value_source("formatter").unwrap() != ValueSource::DefaultValue {
config.formatter = matches.get_one::<PathBuf>("formatter").unwrap().to_owned();
}

if matches.contains_id("deterministic") {
config.deterministic = matches.get_flag("deterministic");
}

if matches.value_source("output_file").unwrap() != ValueSource::DefaultValue {
let output_file = matches.get_one::<PathBuf>("output_file").unwrap();
config.output_file = path_is_stdio(output_file).then(|| output_file.to_owned());
}

if let Some(x) = matches.get_one::<PathBuf>("entry").cloned() {
config.entry = x;
}

Ok(())
}
}

fn path_is_stdio(path: &Path) -> bool {
path.as_os_str().as_encoded_bytes().eq(b"-")
}
Loading

0 comments on commit 21b24e9

Please sign in to comment.