Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["crates/types", "crates/cli"]
members = ["crates/macros", "crates/types", "crates/cli"]
default-members = ["crates/cli"]
resolver = "2"

Expand All @@ -15,6 +15,7 @@ keywords = ["cli", "utils", "wolvenkit", "inkanim", "inkwidget"]
[workspace.dependencies]
clap = { version = "4.5" }
enum_dispatch = "0.3"
inkanim-macros = { path = "crates/macros" }
inkanim-types = { path = "crates/types", version = "0.8.0" }
serde = { version = "1.0" }
serde-aux = "4.7"
Expand Down
5 changes: 4 additions & 1 deletion crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::Parser;

use crate::{list, show, tree, whereis, whois};
use crate::{generate, list, show, tree, whereis, whois};

#[allow(clippy::upper_case_acronyms)]
#[derive(Parser)] // requires `derive` feature
Expand All @@ -20,4 +20,7 @@ pub enum CLI {
/// show json from widget name
#[command(name = "show")]
Show(show::Args),
/// generate reds from widget name
#[command(name = "generate")]
Generate(generate::Args),
}
13 changes: 13 additions & 0 deletions crates/cli/src/generate/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::path::PathBuf;

use crate::args::Files;

#[derive(clap::Args, Debug)]
#[command()]
pub struct Args {
#[command(flatten)]
pub files: Files,
/// .reds output path
#[arg(short, long, value_name = "OUTPUT_REDS")]
pub reds: PathBuf,
}
10 changes: 10 additions & 0 deletions crates/cli/src/generate/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mod args;

pub use args::Args;
use inkanim_types::widget::inkWidgetLibraryResource;

pub(crate) fn generate(args: Args, widget: inkWidgetLibraryResource) {
let Args { .. } = args;
let root_chunk = widget.root_chunk();
let _root = &root_chunk.root_widget.data;
}
4 changes: 4 additions & 0 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use cli::CLI;

mod args;
mod cli;
mod generate;
mod list;
mod read;
mod show;
mod tree;
mod whereis;
mod whois;

use generate::generate;
use list::list;
use read::read;
use show::show;
Expand All @@ -25,6 +27,7 @@ fn main() {
CLI::WhoIs(whois::Args { ref files, .. }) => files,
CLI::WhereIs(whereis::Args { ref files, .. }) => files,
CLI::Show(show::Args { ref files, .. }) => files,
CLI::Generate(generate::Args { ref files, .. }) => files,
};
let (widget, anim) = read(files);
match args {
Expand All @@ -33,5 +36,6 @@ fn main() {
CLI::WhoIs(args) => whois(args, widget.resource(), anim.resource()),
CLI::WhereIs(args) => whereis(args, widget.resource(), anim.resource()),
CLI::Show(args) => show(args, widget.resource()),
CLI::Generate(args) => generate(args, widget.resource()),
};
}
17 changes: 17 additions & 0 deletions crates/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "inkanim-macros"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
keywords.workspace = true

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0"
quote = "1.0.40"
syn = "2.0.104"
161 changes: 161 additions & 0 deletions crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{DeriveInput, Error, parse_macro_input, spanned::Spanned};

#[proc_macro_derive(Reds, attributes(reds))]
pub fn derive_reds(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

let tokens = match &input.data {
syn::Data::Struct(_) => {
let is_class = match is_class(&input) {
Ok(x) => x,
Err(e) => return abort(input, format!("{e}")),
};
if is_class {
derive_reds_class(&input)
} else {
derive_reds_struct(&input)
}
}
syn::Data::Enum(_) => derive_reds_enum(&input),
syn::Data::Union(_) => {
return abort(input, "union is not supported");
}
};

TokenStream::from(tokens)
}

fn is_class(input: &DeriveInput) -> Result<bool, Error> {
for attr in &input.attrs {
if attr.path().is_ident("reds") {
let metalist = attr.meta.require_list()?;
let mut is_class: Option<bool> = None;
metalist.parse_nested_meta(|x| {
if x.path.is_ident("class") {
is_class = Some(true);
} else if x.path.is_ident("struct") {
is_class = Some(false);
}
Ok(())
})?;
if let Some(is_class) = is_class {
return Ok(is_class);
} else {
return Err(fail(input, "expects #[reds(class)] or #[reds(struct)]"));
}
}
}
Ok(true)
}

fn derive_reds_enum(input: &DeriveInput) -> proc_macro2::TokenStream {
match &input.data {
syn::Data::Enum(data_enum) => {
let ty = &input.ident;
let variants = &data_enum
.variants
.iter()
.cloned()
.map(|x| x.ident)
.collect::<Vec<_>>();
quote! {
impl crate::reds::Type for #ty {
const NAME: &str = stringify!(#ty);
}
impl crate::reds::Value for #ty {
fn value(&self) -> std::borrow::Cow<'_, str> {
std::borrow::Cow::Borrowed(match self {
#(Self::#variants => concat!(stringify!(#ty), ".", stringify!(#variants)),)*
})
}
}
}
}
_ => unreachable!(),
}
}
fn derive_reds_class(input: &DeriveInput) -> proc_macro2::TokenStream {
match &input.data {
syn::Data::Struct(data_struct) => {
let ty = &input.ident;
let fields = &data_struct
.fields
.iter()
.cloned()
.map(|x| x.ident.expect("named fields"))
.collect::<Vec<_>>();
quote! {
impl crate::reds::Instantiate for #ty {
fn instantiate(&self, name: &str) -> std::borrow::Cow<'_, str> {
use crate::reds::Setter;
use crate::reds::Value;
let mut me = format!("let {name}: {0} = new {0}();", stringify!(#ty));
let mut acc: String;
#(
me.push_str("\n");
acc = self.setter(name, stringify!(#fields), &self.#fields).into();
me.push_str(&acc);
)*
std::borrow::Cow::Owned(me)
}
}
impl crate::reds::Type for #ty {
const NAME: &str = stringify!(#ty);
}
impl crate::reds::Setter for #ty {
const FIELDS: &[&'static str] = &[
#(stringify!(#fields),)*
];
}
}
}
_ => unreachable!(),
}
}
fn derive_reds_struct(input: &DeriveInput) -> proc_macro2::TokenStream {
match &input.data {
syn::Data::Struct(data_struct) => {
let ty = &input.ident;
let fields = &data_struct
.fields
.iter()
.cloned()
.map(|x| x.ident.expect("named fields"))
.collect::<Vec<_>>();
quote! {
impl crate::reds::Instantiate for #ty {
fn instantiate(&self, name: &str) -> std::borrow::Cow<'_, str> {
use crate::reds::Setter;
use crate::reds::Value;
let mut me = format!("let {name}: {};", stringify!(#ty));
let mut acc: std::borrow::Cow<'_, str>;
#(
me.push_str("\n");
acc = self.setter(name, stringify!(#fields), &self.#fields);
me.push_str(&acc);
)*
std::borrow::Cow::Owned(me)
}
}
impl crate::reds::Type for #ty {
const NAME: &str = stringify!(#ty);
}
impl crate::reds::Setter for #ty {
const FIELDS: &[&'static str] = &[
#(stringify!(#fields),)*
];
}
}
}
_ => unreachable!(),
}
}

fn abort(input: impl Spanned, msg: impl AsRef<str>) -> TokenStream {
fail(input, msg).to_compile_error().into()
}
fn fail(input: impl Spanned, msg: impl AsRef<str>) -> Error {
Error::new(input.span(), msg.as_ref())
}
1 change: 1 addition & 0 deletions crates/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ publish = true

[dependencies]
enum_dispatch.workspace = true
inkanim-macros.workspace = true
serde = { workspace = true, features = ["derive"] }
serde-aux.workspace = true
chrono = { workspace = true, default-features = false, features = [
Expand Down
11 changes: 6 additions & 5 deletions crates/types/src/ink/anim/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

mod display;

use inkanim_macros::Reds;
use serde::{Deserialize, Serialize};
use serde_aux::prelude::*;

Expand All @@ -26,7 +27,7 @@ pub struct OrphanInkAnimInterpolator {
}

/// transparency interpolation direction
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Reds)]
pub enum Fade {
/// transparency interpolates toward `1.`
In,
Expand Down Expand Up @@ -55,7 +56,7 @@ pub enum InkAnimInterpolatorType {
}

/// see [NativeDB](https://nativedb.red4ext.com/inkanimInterpolationDirection)
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Reds)]
pub enum Direction {
To = 0,
From = 1,
Expand All @@ -64,15 +65,15 @@ pub enum Direction {

/// see [NativeDB](https://nativedb.red4ext.com/inkanimInterpolationMode)
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Reds)]
pub enum Mode {
EasyIn = 0,
EasyOut = 1,
EasyInOut = 2,
}

/// see [NativeDB](https://nativedb.red4ext.com/inkanimInterpolationType)
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Reds)]
pub enum Type {
Linear = 0,
Quadratic = 1,
Expand Down Expand Up @@ -135,7 +136,7 @@ pub struct EffectInterpolator {
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Reds)]
pub enum inkEffectType {
ScanlineWipe = 0,
LinearWipe = 1,
Expand Down
Loading