Skip to content

Commit

Permalink
Move impl Drop back into args
Browse files Browse the repository at this point in the history
  • Loading branch information
gustavo-shigueo committed Jun 26, 2024
1 parent 633a11e commit 2b88340
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 161 deletions.
6 changes: 3 additions & 3 deletions cli/src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::process::{Command, Stdio};

use color_eyre::Result;

use crate::{config::Config, path};
use crate::{config::Args, path};

macro_rules! feature {
($cargo_invocation: expr, $args: expr, { $($field: ident => $feature: literal),* $(,)? }) => {
Expand All @@ -16,7 +16,7 @@ macro_rules! feature {
};
}

pub fn invoke(cfg: &Config) -> Result<()> {
pub fn invoke(cfg: &Args) -> Result<()> {
let mut cargo_invocation = Command::new("cargo");

cargo_invocation
Expand All @@ -31,7 +31,7 @@ pub fn invoke(cfg: &Config) -> Result<()> {
} else {
Stdio::piped()
})
.env("TS_RS_EXPORT_DIR", path::absolute(&cfg.output_directory())?);
.env("TS_RS_EXPORT_DIR", path::absolute(cfg.output_directory())?);

for (rust, ts) in &cfg.overrides {
let env = format!("TS_RS_INTERNAL_OVERRIDE_{rust}");
Expand Down
297 changes: 153 additions & 144 deletions cli/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,144 +1,153 @@
use std::{
collections::HashMap,
path::{Path, PathBuf},
};

use clap::Parser;
use color_eyre::{eyre::bail, owo_colors::OwoColorize, Result};
use serde::Deserialize;

#[derive(Parser, Debug)]
#[allow(clippy::struct_excessive_bools)]
pub struct Args {
/// Path to the `ts-rs` config file
#[arg(long)]
pub config: Option<PathBuf>,

/// Defines where your TS bindings will be saved by setting `TS_RS_EXPORT_DIR`
#[arg(long, short)]
pub output_directory: Option<PathBuf>,

/// Disables warnings caused by using serde attributes that ts-rs cannot process
#[arg(long)]
pub no_warnings: bool,

/// Adds the ".js" extension to import paths
#[arg(long)]
pub esm_imports: bool,

/// Formats the generated TypeScript files
#[arg(long)]
pub format: bool,

/// Generates an index.ts file in your --output-directory that re-exports all
/// types generated by ts-rs
#[arg(long = "index")]
pub generate_index_ts: bool,

/// Generates only a single index.ts file in your --output-directory that
/// contains all exported types
#[arg(long = "merge")]
pub merge_files: bool,

/// Do not capture `cargo test`'s output, and pass --nocapture to the test binary
#[arg(long = "nocapture")]
pub no_capture: bool,
}

// keeping this separate from `Args` for now :shrug:
#[derive(Default, Deserialize)]
#[serde(deny_unknown_fields, default, rename_all = "kebab-case")]
#[allow(clippy::struct_excessive_bools)]
pub struct Config {
// type overrides for types implemented inside ts-rs.
pub overrides: HashMap<String, String>,
pub output_directory: Option<PathBuf>,
pub no_warnings: bool,
pub esm_imports: bool,
pub format: bool,

#[serde(rename = "index")]
pub generate_index_ts: bool,

#[serde(rename = "merge")]
pub merge_files: bool,

#[serde(rename = "nocapture")]
pub no_capture: bool,
}

impl Config {
pub fn load() -> Result<Self> {
let args = Args::parse();

let cfg = Self::load_from_file(args.config.as_deref())?.merge(args);
cfg.verify()?;
Ok(cfg)
}

pub fn output_directory(&self) -> &Path {
self.output_directory
.as_deref()
.expect("Output directory must not be `None`")
}

fn load_from_file(path: Option<&Path>) -> Result<Self> {
if let Some(path) = path {
if !path.is_file() {
bail!("The provided path doesn't exist");
}

let content = std::fs::read_to_string(path)?;
return Ok(toml::from_str(&content)?);
}

// TODO: from where do we actually load the config?
let path = Path::new("./ts-rs.toml");
if !path.is_file() {
return Ok(Self::default());
}
let content = std::fs::read_to_string(path)?;
Ok(toml::from_str(&content)?)
}

fn verify(&self) -> Result<()> {
if self.merge_files && self.generate_index_ts {
bail!(
"{}: --index is not compatible with --merge",
"Error".bold().red()
);
}

if self.output_directory.is_none() {
bail!("{}: You must provide the output diretory, either through the config file or the --output-directory flag", "Error".bold().red())
}

Ok(())
}

fn merge(
mut self,
Args {
config: _,
output_directory,
no_warnings,
esm_imports,
format,
generate_index_ts,
merge_files,
no_capture,
}: Args,
) -> Self {
// QUESTION: This gives the CLI flag priority over the config file's value,
// is this the correct order?
self.output_directory = output_directory.or(self.output_directory);

self.no_warnings |= no_warnings;
self.esm_imports |= esm_imports;
self.format |= format;
self.generate_index_ts |= generate_index_ts;
self.merge_files |= merge_files;
self.no_capture |= no_capture;
self
}
}
use std::{
collections::HashMap,
path::{Path, PathBuf},
};

use clap::Parser;
use color_eyre::{eyre::bail, owo_colors::OwoColorize, Result};
use serde::Deserialize;

#[derive(Parser, Debug)]
#[allow(clippy::struct_excessive_bools)]
pub struct Args {
#[clap(skip)]
pub overrides: HashMap<String, String>,

/// Path to the `ts-rs` config file
#[arg(long)]
pub config: Option<PathBuf>,

/// Defines where your TS bindings will be saved by setting `TS_RS_EXPORT_DIR`
#[arg(long, short)]
pub output_directory: Option<PathBuf>,

/// Disables warnings caused by using serde attributes that ts-rs cannot process
#[arg(long)]
pub no_warnings: bool,

/// Adds the ".js" extension to import paths
#[arg(long)]
pub esm_imports: bool,

/// Formats the generated TypeScript files
#[arg(long)]
pub format: bool,

/// Generates an index.ts file in your --output-directory that re-exports all
/// types generated by ts-rs
#[arg(long = "index")]
pub generate_index_ts: bool,

/// Generates only a single index.ts file in your --output-directory that
/// contains all exported types
#[arg(long = "merge")]
pub merge_files: bool,

/// Do not capture `cargo test`'s output, and pass --nocapture to the test binary
#[arg(long = "nocapture")]
pub no_capture: bool,
}

// keeping this separate from `Args` for now :shrug:
#[derive(Default, Deserialize)]
#[serde(deny_unknown_fields, default, rename_all = "kebab-case")]
#[allow(clippy::struct_excessive_bools)]
pub struct Config {
/// Type overrides for types implemented inside ts-rs.
pub overrides: HashMap<String, String>,
pub output_directory: Option<PathBuf>,
pub no_warnings: bool,
pub esm_imports: bool,
pub format: bool,

#[serde(rename = "index")]
pub generate_index_ts: bool,

#[serde(rename = "merge")]
pub merge_files: bool,

#[serde(rename = "nocapture")]
pub no_capture: bool,
}

impl Args {
pub fn load() -> Result<Self> {
let mut args = Self::parse();

let cfg = Config::load_from_file(args.config.as_deref())?;

args.merge(cfg);
args.verify()?;

Ok(args)
}

pub fn output_directory(&self) -> &Path {
self.output_directory
.as_deref()
.expect("Output directory must not be `None`")
}

fn verify(&self) -> Result<()> {
if self.merge_files && self.generate_index_ts {
bail!(
"{}: --index is not compatible with --merge",
"Error".bold().red()
);
}

if self.output_directory.is_none() {
bail!("{}: You must provide the output diretory, either through the config file or the --output-directory flag", "Error".bold().red())
}

Ok(())
}

fn merge(
&mut self,
Config {
overrides,
output_directory,
no_warnings,
esm_imports,
format,
generate_index_ts,
merge_files,
no_capture,
}: Config,
) {
// QUESTION: This gives the CLI flag priority over the config file's value,
// is this the correct order?
self.output_directory = output_directory.or_else(|| self.output_directory.clone());

self.overrides = overrides;
self.no_warnings |= no_warnings;
self.esm_imports |= esm_imports;
self.format |= format;
self.generate_index_ts |= generate_index_ts;
self.merge_files |= merge_files;
self.no_capture |= no_capture;
}
}

impl Config {
fn load_from_file(path: Option<&Path>) -> Result<Self> {
if let Some(path) = path {
if !path.is_file() {
bail!("The provided path doesn't exist");
}

let content = std::fs::read_to_string(path)?;
return Ok(toml::from_str(&content)?);
}

// TODO: from where do we actually load the config?
let path = Path::new("./ts-rs.toml");
if !path.is_file() {
return Ok(Self::default());
}

let content = std::fs::read_to_string(path)?;
Ok(toml::from_str(&content)?)
}
}
Loading

0 comments on commit 2b88340

Please sign in to comment.