diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb86b42..6ecd889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,9 @@ name: CI on: push: - branches: - - master - pull_request: branches: - - master + - main jobs: build: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 502a5ff..19258c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -# Copyright 2022-2023, axodotdev +# Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: @@ -6,9 +6,9 @@ # * checks for a Git Tag that looks like a release # * builds artifacts with cargo-dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip -# * on success, uploads the artifacts to a Github Release +# * on success, uploads the artifacts to a GitHub Release # -# Note that the Github Release will be created with a generated +# Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release @@ -31,7 +31,7 @@ permissions: # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will -# spin up, creating an independent announcement for each one. However Github +# spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # @@ -41,7 +41,6 @@ on: push: tags: - '**[0-9]+.[0-9]+.[0-9]+*' - pull_request: jobs: # Run 'cargo dist plan' (or host) to determine what tasks we need to do @@ -62,7 +61,7 @@ jobs: # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.2/cargo-dist-installer.sh | sh" # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. @@ -109,6 +108,8 @@ jobs: with: submodules: recursive - uses: swatinem/rust-cache@v2 + with: + key: ${{ join(matrix.targets, '-') }} - name: Install cargo-dist run: ${{ matrix.install_dist }} # Get the dist-manifest @@ -135,7 +136,7 @@ jobs: run: | # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" - jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT" + jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" @@ -162,7 +163,7 @@ jobs: submodules: recursive - name: Install cargo-dist shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.2/cargo-dist-installer.sh | sh" # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 @@ -178,7 +179,7 @@ jobs: # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" - jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT" + jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" @@ -207,7 +208,7 @@ jobs: with: submodules: recursive - name: Install cargo-dist - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.12.0/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.13.2/cargo-dist-installer.sh | sh" # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 @@ -215,7 +216,7 @@ jobs: pattern: artifacts-* path: target/distrib/ merge-multiple: true - # This is a harmless no-op for Github Releases, hosting for that happens in "announce" + # This is a harmless no-op for GitHub Releases, hosting for that happens in "announce" - id: host shell: bash run: | @@ -230,7 +231,7 @@ jobs: name: artifacts-dist-manifest path: dist-manifest.json - # Create a Github Release while uploading all files to it + # Create a GitHub Release while uploading all files to it announce: needs: - plan @@ -246,7 +247,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive - - name: "Download Github Artifacts" + - name: "Download GitHub Artifacts" uses: actions/download-artifact@v4 with: pattern: artifacts-* @@ -256,7 +257,7 @@ jobs: run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - - name: Create Github Release + - name: Create GitHub Release uses: ncipollo/release-action@v1 with: tag: ${{ needs.plan.outputs.tag }} diff --git a/Cargo.lock b/Cargo.lock index b3e24cf..e5fdcea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "asphalt" -version = "0.4.2" +version = "0.5.0" dependencies = [ "anyhow", "blake3", diff --git a/Cargo.toml b/Cargo.toml index a549173..c1ca81b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "asphalt" -version = "0.4.2" +version = "0.5.0" edition = "2021" license = "MIT" authors = ["Jack T "] @@ -32,20 +32,23 @@ lto = "thin" # Config for 'cargo dist' [workspace.metadata.dist] # The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) -cargo-dist-version = "0.12.0" +cargo-dist-version = "0.13.2" # CI backends to support ci = ["github"] # The installers to generate for each app installers = [] # Target platforms to build apps for (Rust target-triple syntax) targets = [ - "aarch64-apple-darwin", - "x86_64-apple-darwin", - "x86_64-unknown-linux-gnu", - "x86_64-pc-windows-msvc", + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "x86_64-pc-windows-msvc", ] # Publish jobs to run in CI pr-run-mode = "skip" +# The archive format to use for windows builds (defaults .zip) windows-archive = ".zip" +# The archive format to use for non-windows builds (defaults .tar.xz) unix-archive = ".zip" +# Checksums to generate for each App checksum = "false" diff --git a/README.md b/README.md index 798c4dd..eecba01 100644 --- a/README.md +++ b/README.md @@ -35,15 +35,20 @@ Asphalt is configured with a project file called `asphalt.toml`. It is required ```toml asset_dir = "test/" write_dir = "output/" + +[codegen] typescript = true luau = true +style = "flat" +output_name = "assets" [creator] type = "user" id = 9670971 -[existing."test/online_asset.ogg"] -id = 583095803 +[existing] +"test/some_sound_on_roblox.ogg" = { id = 123456789 } +"test/some_image_on_roblox.png" = { id = 987654321 } ``` @@ -56,12 +61,8 @@ id = 583095803 - The directory to output the generated code to. This should probably be somewhere in your game's source folder. - `creator`: Creator - The Roblox creator to upload the assets under. -- `typescript`: boolean (optional) - - Generate a Typescript definition file. -- `luau`: boolean (optional) - - Use the `luau` file extension. -- `output_name`: string (optional) - - The name for the generated files. Defaults to `assets`. +- `codegen`: Codegen + - Code generation options. - `existing`: map (optional) #### Creator @@ -69,6 +70,17 @@ id = 583095803 - `type`: "user" or "group" - `id`: number +#### Codegen + +- `typescript`: boolean (optional) + - Generate a Typescript definition file. +- `luau`: boolean (optional) + - Use the `luau` file extension. +- `style`: "flat" | "nested" (optional) + - The code-generation style to use. Defaults to `flat`. If you would like to have an experience similar to [Tarmac](https://github.com/rojo-rbx/tarmac), use `nested`. +- `output_name`: string (optional) + - The name for the generated files. Defaults to `assets`. + #### ExistingAsset - `id`: number @@ -80,3 +92,5 @@ Just run `asphalt` and make sure you have a config file as specified above. When ## API Key You will need an API key to run Asphalt. You can specify this using the `--api-key` argument, or the `ASPHALT_API_KEY` environment variable. + +You can get one from the [Creator Dashboard](https://create.roblox.com/dashboard/credentials). Make sure you select the correct group and Asset-related permissions. diff --git a/src/codegen.rs b/src/codegen/flat.rs similarity index 56% rename from src/codegen.rs rename to src/codegen/flat.rs index 86a2c4d..84550d7 100644 --- a/src/codegen.rs +++ b/src/codegen/flat.rs @@ -47,46 +47,3 @@ pub fn generate_ts( output_dir, interface, output_dir )) } - -#[cfg(test)] -mod tests { - use std::collections::BTreeMap; - - use crate::{FileEntry, LockFile}; - - fn test_lockfile() -> LockFile { - let mut entries = BTreeMap::new(); - entries.insert( - "assets/foo.png".to_string(), - FileEntry { - asset_id: 1, - hash: "a".to_string(), - }, - ); - entries.insert( - "assets/bar/baz.png".to_string(), - FileEntry { - asset_id: 2, - hash: "b".to_string(), - }, - ); - - LockFile { entries } - } - - #[test] - fn generate_lua() { - let lockfile = test_lockfile(); - - let lua = super::generate_lua(&lockfile, "assets").unwrap(); - assert_eq!(lua, "return {\n\t[\"/bar/baz.png\"] = \"rbxassetid://2\",\n\t[\"/foo.png\"] = \"rbxassetid://1\"\n}"); - } - - #[test] - fn generate_ts() { - let lockfile = test_lockfile(); - - let ts = super::generate_ts(&lockfile, "assets", "assets").unwrap(); - assert_eq!(ts, "declare const assets: {\n\t\"/bar/baz.png\": string,\n\t\"/foo.png\": string\n}\nexport = assets"); - } -} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs new file mode 100644 index 0000000..98db9b3 --- /dev/null +++ b/src/codegen/mod.rs @@ -0,0 +1,89 @@ +use crate::{config::StyleType, LockFile}; + +mod flat; +mod nested; + +pub fn generate_lua( + lockfile: &LockFile, + strip_dir: &str, + style: &StyleType, +) -> anyhow::Result { + match style { + StyleType::Flat => flat::generate_lua(lockfile, strip_dir), + StyleType::Nested => nested::generate_lua(lockfile, strip_dir), + } +} + +pub fn generate_ts( + lockfile: &LockFile, + strip_dir: &str, + output_dir: &str, + style: &StyleType, +) -> anyhow::Result { + match style { + StyleType::Flat => flat::generate_ts(lockfile, strip_dir, output_dir), + StyleType::Nested => nested::generate_ts(lockfile, strip_dir, output_dir), + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use crate::{FileEntry, LockFile}; + + fn test_lockfile() -> LockFile { + let mut entries = BTreeMap::new(); + entries.insert( + "assets/foo.png".to_string(), + FileEntry { + asset_id: 1, + hash: "a".to_string(), + }, + ); + entries.insert( + "assets/bar/baz.png".to_string(), + FileEntry { + asset_id: 2, + hash: "b".to_string(), + }, + ); + LockFile { entries } + } + + #[test] + fn generate_lua() { + let lockfile = test_lockfile(); + + let lua = super::flat::generate_lua(&lockfile, "assets").unwrap(); + assert_eq!(lua, "return {\n\t[\"/bar/baz.png\"] = \"rbxassetid://2\",\n\t[\"/foo.png\"] = \"rbxassetid://1\"\n}"); + } + + #[test] + fn generate_ts() { + let lockfile = test_lockfile(); + + let ts = super::flat::generate_ts(&lockfile, "assets", "assets").unwrap(); + assert_eq!(ts, "declare const assets: {\n\t\"/bar/baz.png\": string,\n\t\"/foo.png\": string\n}\nexport = assets"); + } + + #[test] + fn generate_lua_nested() { + let lockfile = test_lockfile(); + + let lua = super::nested::generate_lua(&lockfile, "assets").unwrap(); + assert_eq!( + lua, + "return {\n bar = {\n [\"baz.png\"] = \"rbxassetid://2\",\n },\n [\"foo.png\"] = \"rbxassetid://1\",\n}"); + } + + #[test] + fn generate_ts_nested() { + let lockfile = test_lockfile(); + + let ts = super::nested::generate_ts(&lockfile, "assets", "assets").unwrap(); + assert_eq!( + ts, + "declare const assets: {\n bar: {\n \"baz.png\": \"rbxassetid://2\",\n },\n \"foo.png\": \"rbxassetid://1\",\n}\nexport = assets"); + } +} diff --git a/src/codegen/nested/ast.rs b/src/codegen/nested/ast.rs new file mode 100644 index 0000000..cdda073 --- /dev/null +++ b/src/codegen/nested/ast.rs @@ -0,0 +1,234 @@ +use std::fmt::{self, Write}; + +macro_rules! proxy_display { + ( $target: ty ) => { + impl fmt::Display for $target { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + let mut stream = AstStream::new(output, &self.1); + AstFormat::fmt_ast(self, &mut stream) + } + } + }; +} + +trait AstFormat { + fn fmt_ast(&self, output: &mut AstStream) -> fmt::Result; + fn fmt_key(&self, output: &mut AstStream<'_, '_>) -> fmt::Result { + write!(output, "[")?; + self.fmt_ast(output)?; + write!(output, "]") + } +} + +#[derive(Debug)] +pub(crate) enum AstTarget { + Lua, + Typescript { output_dir: String }, +} + +pub(crate) struct AstStream<'a, 'b> { + number_of_spaces: usize, + indents: usize, + is_start_of_line: bool, + writer: &'a mut (dyn Write), + target: &'b AstTarget, +} + +impl<'a, 'b> AstStream<'a, 'b> { + pub fn new(writer: &'a mut (dyn fmt::Write + 'a), target: &'b AstTarget) -> Self { + Self { + number_of_spaces: 4, + indents: 0, + is_start_of_line: true, + writer, + target, + } + } + + fn indent(&mut self) { + self.indents += 1 + } + + fn unindent(&mut self) { + if self.indents > 0 { + self.indents -= 1 + } + } + + fn begin_line(&mut self) -> fmt::Result { + self.is_start_of_line = true; + self.writer.write_char('\n') + } +} + +impl Write for AstStream<'_, '_> { + fn write_str(&mut self, value: &str) -> fmt::Result { + let mut is_first_line = true; + + for line in value.split('\n') { + if is_first_line { + is_first_line = false; + } else { + self.begin_line()?; + } + + if !line.is_empty() { + if self.is_start_of_line { + self.is_start_of_line = false; + self.writer.write_str(&format!( + "{: >1$}", + "", + self.number_of_spaces * self.indents + ))?; + } + + self.writer.write_str(line)?; + } + } + + Ok(()) + } +} + +proxy_display!(ReturnStatement); + +#[derive(Debug)] +pub(crate) struct ReturnStatement(pub Expression, pub AstTarget); + +impl AstFormat for ReturnStatement { + fn fmt_ast(&self, output: &mut AstStream) -> fmt::Result { + match output.target { + AstTarget::Lua => { + write!(output, "return ") + } + AstTarget::Typescript { output_dir } => { + write!(output, "declare const {output_dir}: ") + } + }?; + + let result = self.0.fmt_ast(output); + if let AstTarget::Typescript { output_dir } = output.target { + write!(output, "\nexport = {output_dir}")? + } + result + } +} + +#[derive(Debug)] +pub(crate) enum Expression { + String(String), + Table(Table), +} + +impl Expression { + pub fn table(expressions: Vec<(Expression, Expression)>) -> Self { + Self::Table(Table { expressions }) + } +} + +impl AstFormat for Expression { + fn fmt_ast(&self, output: &mut AstStream) -> fmt::Result { + match self { + Self::Table(val) => val.fmt_ast(output), + Self::String(val) => val.fmt_ast(output), + } + } + + fn fmt_key(&self, output: &mut AstStream<'_, '_>) -> fmt::Result { + match self { + Self::Table(val) => val.fmt_key(output), + Self::String(val) => val.fmt_key(output), + } + } +} + +#[derive(Debug)] +pub(crate) struct Table { + pub expressions: Vec<(Expression, Expression)>, +} + +impl AstFormat for Table { + fn fmt_ast(&self, output: &mut AstStream<'_, '_>) -> fmt::Result { + let assignment = match output.target { + AstTarget::Lua => " = ", + AstTarget::Typescript { .. } => ": ", + }; + + writeln!(output, "{{")?; + output.indent(); + + for (key, value) in &self.expressions { + key.fmt_key(output)?; + write!(output, "{assignment}")?; + value.fmt_ast(output)?; + writeln!(output, ",")?; + } + + output.unindent(); + write!(output, "}}") + } +} + +impl AstFormat for String { + fn fmt_ast(&self, output: &mut AstStream) -> fmt::Result { + write!(output, "\"{}\"", self) + } + + fn fmt_key(&self, output: &mut AstStream<'_, '_>) -> fmt::Result { + if is_valid_identifier(self) { + write!(output, "{}", self) + } else { + match output.target { + AstTarget::Lua => write!(output, "[\"{}\"]", self), + AstTarget::Typescript { .. } => write!(output, "\"{}\"", self), + } + } + } +} + +impl From for Expression { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From<&'_ String> for Expression { + fn from(value: &String) -> Self { + Self::String(value.clone()) + } +} + +impl From<&'_ str> for Expression { + fn from(value: &str) -> Self { + Self::String(value.to_owned()) + } +} + +impl From for Expression { + fn from(value: Table) -> Self { + Self::Table(value) + } +} + +fn is_valid_ident_char_start(value: char) -> bool { + value.is_ascii_alphabetic() || value == '_' +} + +fn is_valid_ident_char(value: char) -> bool { + value.is_ascii_alphanumeric() || value == '_' +} + +fn is_valid_identifier(value: &str) -> bool { + let mut chars = value.chars(); + + match chars.next() { + Some(first) => { + if !is_valid_ident_char_start(first) { + return false; + } + } + None => return false, + } + + chars.all(is_valid_ident_char) +} diff --git a/src/codegen/nested/mod.rs b/src/codegen/nested/mod.rs new file mode 100644 index 0000000..6d0c957 --- /dev/null +++ b/src/codegen/nested/mod.rs @@ -0,0 +1,115 @@ +mod ast; + +use anyhow::{bail, Context}; +use std::collections::BTreeMap; +use std::{path::Component as PathComponent, path::Path}; + +use crate::LockFile; +use ast::{AstTarget, Expression, ReturnStatement}; +use std::fmt::Write; + +use self::types::NestedTable; + +pub(crate) mod types { + use std::collections::BTreeMap; + + use crate::FileEntry; + + #[derive(Debug)] + pub enum NestedTable<'a> { + Folder(BTreeMap>), + File(&'a FileEntry), + } +} + +/// Recursively builds a **[`NestedTable`]** (normally a root table) into expressions that can be evaluated in the `ast`. +fn build_table(entry: &NestedTable) -> Expression { + match entry { + NestedTable::Folder(entries) => Expression::table( + entries + .iter() + .map(|(component, entry)| (component.into(), build_table(entry))) + .collect(), + ), + NestedTable::File(file) => Expression::String(format!("rbxassetid://{}", file.asset_id)), + } +} + +/** + * Creates expressions based on the **[`LockFile`]**, and will strip the prefix + * and iterate through every file entry and build a table for code generation. +*/ +fn generate_expressions(lockfile: &LockFile, strip_dir: &str) -> anyhow::Result { + let mut root: BTreeMap> = BTreeMap::new(); + + for (file_path, file_entry) in lockfile.entries.iter() { + let mut components = vec![]; + let path = Path::new(file_path) + .strip_prefix(strip_dir) + .context("Failed to strip directory prefix")?; + + for component in path.components() { + match component { + PathComponent::RootDir | PathComponent::Prefix(..) | PathComponent::Normal(..) => { + components.push( + component + .as_os_str() + .to_str() + .context("Failed to resolve path component")?, + ) + } + PathComponent::ParentDir => { + if components.pop().is_none() { + bail!("Failed to resolve parent directory") + } + } + _ => {} + } + } + + let mut current_directory = &mut root; + for (index, &component) in components.iter().enumerate() { + // last component is assumed to be a file. + if index == components.len() - 1 { + if current_directory.get_mut(component).is_none() { + current_directory.insert(component.to_owned(), NestedTable::File(file_entry)); + }; + } else if let NestedTable::Folder(entries) = current_directory + .entry(component.to_owned()) + .or_insert_with(|| NestedTable::Folder(BTreeMap::new())) + { + current_directory = entries; + } else { + unreachable!() + } + } + } + + Ok(build_table(&NestedTable::Folder(root))) +} + +pub fn generate_lua(lockfile: &LockFile, strip_dir: &str) -> anyhow::Result { + generate_code( + generate_expressions(lockfile, strip_dir).context("Failed to create nested expressions")?, + AstTarget::Lua, + ) +} + +pub fn generate_ts( + lockfile: &LockFile, + strip_dir: &str, + output_dir: &str, +) -> anyhow::Result { + generate_code( + generate_expressions(lockfile, strip_dir).context("Failed to create nested expressions")?, + AstTarget::Typescript { + output_dir: output_dir.to_owned(), + }, + ) +} + +fn generate_code(expression: Expression, target: AstTarget) -> anyhow::Result { + let mut buffer = String::new(); + write!(buffer, "{}", ReturnStatement(expression, target))?; + Ok(buffer) +} diff --git a/src/config.rs b/src/config.rs index 414b586..4e3cc5a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,13 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; +#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum StyleType { + Flat, + Nested, +} + #[derive(Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "snake_case")] pub enum CreatorType { @@ -22,13 +29,18 @@ pub struct ExistingAsset { } #[derive(Debug, Deserialize, Serialize)] -pub struct Config { - pub asset_dir: String, - pub write_dir: String, - pub creator: Creator, +pub struct CodegenConfig { pub output_name: Option, pub typescript: Option, pub luau: Option, + pub style: Option, +} +#[derive(Debug, Deserialize, Serialize)] +pub struct Config { + pub asset_dir: String, + pub write_dir: String, + pub creator: Creator, + pub codegen: CodegenConfig, pub existing: Option>, } diff --git a/src/main.rs b/src/main.rs index 79415fa..b2c2b0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -178,7 +178,7 @@ async fn main() -> anyhow::Result<()> { })); let lua_filename = format!("{}.{}", state.output_name, state.lua_extension); - let lua_output = generate_lua(&state.new_lockfile, asset_dir_str); + let lua_output = generate_lua(&state.new_lockfile, asset_dir_str, &state.style); write(Path::new(&state.write_dir).join(lua_filename), lua_output?) .await @@ -190,6 +190,7 @@ async fn main() -> anyhow::Result<()> { &state.new_lockfile, asset_dir_str, state.output_name.as_str(), + &state.style, ); write(Path::new(&state.write_dir).join(ts_filename), ts_output?) diff --git a/src/state.rs b/src/state.rs index 254e7b6..4ffd0a8 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,6 +1,6 @@ use crate::{ args::Args, - config::{Config, CreatorType, ExistingAsset}, + config::{Config, CreatorType, ExistingAsset, StyleType}, LockFile, }; use anyhow::Context; @@ -31,9 +31,11 @@ pub struct State { pub api_key: String, pub creator: AssetCreator, + pub typescript: bool, pub output_name: String, pub lua_extension: String, + pub style: StyleType, pub font_db: Database, @@ -66,14 +68,16 @@ impl State { let write_dir = PathBuf::from(write_dir); let output_name = config + .codegen .output_name .as_ref() .unwrap_or(&"assets".to_string()) .to_string(); - let typescript = config.typescript.unwrap_or(false); + let typescript = config.codegen.typescript.unwrap_or(false); + let style = config.codegen.style.unwrap_or(StyleType::Flat); - let lua_extension = String::from(if config.luau.unwrap_or(false) { + let lua_extension = String::from(if config.codegen.luau.unwrap_or(false) { "luau" } else { "lua" @@ -101,6 +105,7 @@ impl State { typescript, output_name, lua_extension, + style, font_db, existing_lockfile, new_lockfile,