Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add show command #36

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7e8da94
:construction: work in progress
Roms1383 Feb 4, 2024
31feec3
:construction: work in progress
Roms1383 Feb 4, 2024
c007bac
:alembic: codegen from derive types
Roms1383 Feb 6, 2024
68103f3
:alembic: codegen from derive types
Roms1383 Feb 6, 2024
feec078
:construction: work in progress
Roms1383 Feb 7, 2024
fd4ea0d
:construction: work in progress : tuple and default values
Roms1383 Feb 7, 2024
a27f20f
:construction: work in progress : tuple and default values
Roms1383 Feb 7, 2024
d1fc991
:construction: work in progress : split into leaf/compound
Roms1383 Feb 7, 2024
59dfc47
:construction: work in progress : add children
Roms1383 Feb 7, 2024
cf1ccc3
:construction: work in progress : fix definition
Roms1383 Feb 7, 2024
17afad2
:construction: work in progress : fixes
Roms1383 Feb 7, 2024
68d9f67
:construction: work in progress : refactor cname and resourcepath
Roms1383 Feb 7, 2024
5c745a1
:construction: work in progress : fixes
Roms1383 Feb 8, 2024
daee328
:construction: work in progress : cleanup
Roms1383 Feb 8, 2024
3806db3
:construction: work in progress: ongoing setters
Roms1383 Feb 9, 2024
5a2495b
Merge branch 'main' into feat/show
Roms1383 Feb 9, 2024
94350e8
Merge branch 'main' into feat/show
Roms1383 Mar 14, 2024
0ffa99c
Merge branch 'main' into feat/show
Roms1383 Aug 3, 2024
b91d2ef
Merge branch 'main' into feat/show
Roms1383 Oct 28, 2024
e0533e8
Merge branch 'feat/show' of https://github.com/cyb3rpsych0s1s/inkanim…
Roms1383 Oct 28, 2024
94525e5
:arrow_up: update red4ext-rs
Roms1383 Oct 28, 2024
2784a21
:alien: update from latest red4ext-rs changes
Roms1383 Oct 28, 2024
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
571 changes: 350 additions & 221 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
inkanim-macros = { path = "./macros"}
enum_dispatch = "0.3.13"
serde = { version = "1.0.213", features = ["derive"] }
serde-aux = "4.5.0"
Expand All @@ -27,4 +28,7 @@ chrono = { version = "0.4.38", default-features = false, features = [
"serde",
] }
clap = { version = "4.5.20", features = ["derive"] }
const-str = "0.5.6"
convert_case = "0.6.0"
red4ext-rs = { git = "https://github.com/jac3km4/red4ext-rs.git", rev = "v0.9.1" }
term-table = "1.4.0"
20 changes: 20 additions & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "inkanim-macros"
version = "0.3.0"
edition = "2021"
authors = ["Roms1383"]
license = "MIT OR Apache-2.0"
categories = ["command-line-utilities"]
repository = "https://github.com/cyb3rpsych0s1s/inkanim"
publish = false

[lib]
proc-macro = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
syn = "2"
proc-macro2 = "1"
quote = "1"
red4ext-rs = { git = "https://github.com/jac3km4/red4ext-rs.git", rev = "v0.9.1" }
168 changes: 168 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use proc_macro::TokenStream;
use syn::{parse_macro_input, spanned::Spanned, Fields};
use quote::quote;

#[proc_macro_derive(RedsWidgetCompound)]
pub fn derive_reds_widget_compound(item: TokenStream) -> TokenStream {
let syn::DeriveInput{ ident, data, .. } = parse_macro_input!(item as syn::DeriveInput);
match &data {
syn::Data::Struct(data) => derive_reds_widget_compound_for_struct(&ident, data),
syn::Data::Enum(_) | syn::Data::Union(_) => syn::Error::new(ident.span(), "RedsWidgetCompound cannot be derived neither on union nor enum")
.to_compile_error()
.into(),
}
}

#[proc_macro_derive(RedsWidgetLeaf)]
pub fn derive_reds_widget_leaf(item: TokenStream) -> TokenStream {
let syn::DeriveInput{ ident, data, .. } = parse_macro_input!(item as syn::DeriveInput);
match &data {
syn::Data::Struct(data) => derive_reds_widget_leaf_for_struct(&ident, data),
syn::Data::Enum(_) | syn::Data::Union(_) => syn::Error::new(ident.span(), "RedsWidgetLeaf cannot be derived neither on union nor enum")
.to_compile_error()
.into(),
}
}

#[proc_macro_derive(RedsValue)]
pub fn derive_reds_value(item: TokenStream) -> TokenStream {
let syn::DeriveInput{ ident, data, .. } = parse_macro_input!(item as syn::DeriveInput);
match &data {
syn::Data::Enum(data) => derive_reds_value_for_enum(&ident, data),
syn::Data::Struct(data) => derive_reds_value_for_struct(&ident, data),
syn::Data::Union(_) => syn::Error::new(ident.span(), "RedsValue cannot be derived on union")
.to_compile_error()
.into(),
}
}

/// used with Redscript inkWidget class descendants
fn derive_reds_widget_leaf_for_struct(name: &syn::Ident, r#struct: &syn::DataStruct) -> TokenStream {
let oneliners = r#struct.fields.iter().map(|x| x.ident.clone());
quote! {
impl crate::RedsWidgetLeaf for #name {
fn reds_widget_leaf(&self, instance: &str, parent: Option<&str>) -> String {
use ::red4ext_rs::NativeRepr;
use crate::RedsValue;
use crate::IsDefault;
use convert_case::{Case, Casing};
let mut steps = vec![];
steps.push(format!("let {} = new {}();", instance, Self::NAME));
#(
if !self.#oneliners.is_default() {
steps.push(::std::format!("{}.Set{}({});", instance, ::std::stringify!(#oneliners).to_case(Case::Pascal), self.#oneliners.reds_value()));
}
)*
steps.join("\n")
}
}
}.into()
}

/// used with Redscript native struct
fn derive_reds_value_for_struct(name: &syn::Ident, r#struct: &syn::DataStruct) -> TokenStream {
let is_tuple = match r#struct.fields {
Fields::Unnamed(_) => true,
_ => false,
};
if is_tuple {
let indexes = r#struct.fields.iter().enumerate().map(|(index, _)| syn::Index::from(index));
return quote! {
impl crate::RedsValue for #name {
fn reds_value(&self) -> String {
use ::red4ext_rs::NativeRepr;
let mut args = Vec::<String>::new();
#(
args.push(self.#indexes.reds_value());
)*
format!("new {}({})", Self::NAME, args.join(", "))
}
}
}.into()
}
let fields = r#struct.fields.iter().map(|x| x.ident.clone());
quote! {
impl crate::RedsValue for #name {
fn reds_value(&self) -> String {
use ::red4ext_rs::NativeRepr;
let mut args = Vec::<String>::new();
#(
args.push(self.#fields.reds_value());
)*
format!("new {}({})", Self::NAME, args.join(", "))
}
}
}.into()
}

/// used with Redscript enums
fn derive_reds_value_for_enum(name: &syn::Ident, r#enum: &syn::DataEnum) -> TokenStream {
let matches = r#enum.variants.iter().map(|x| {
let variant = &x.ident;
if x.fields.len() > 0 {
return syn::Error::new(variant.span(), "RedsValue can only be derived on enum with unit variants")
.to_compile_error()
.into()
}
quote!{
#name::#variant => ::std::format!("{}.{}", Self::NAME, ::std::stringify!(#variant))
}
});
quote! {
impl crate::RedsValue for #name {
fn reds_value(&self) -> String {
use ::red4ext_rs::NativeRepr;
match self {
#(#matches),*
}
}
}
}.into()
}

fn derive_reds_widget_compound_for_struct(name: &syn::Ident, r#struct: &syn::DataStruct) -> TokenStream {
let oneliners = r#struct.fields
.iter()
.filter(|x| x.ident != Some(syn::Ident::new(::std::stringify!(children), x.span())))
.map(|x| x.ident.clone());
quote! {
impl crate::RedsWidgetCompound for #name {
fn reds_widget_compound(&self, instance: &str, parent: Option<&str>) -> String {
use ::red4ext_rs::NativeRepr;
use crate::widget::layout::inkEChildOrder;
use crate::RedsValue;
use crate::IsDefault;
use convert_case::{Case, Casing};
let mut steps = vec![];
steps.push(::std::format!("let {} = new {}();", instance, Self::NAME));
#(
if !self.#oneliners.is_default() {
steps.push(::std::format!("{}.Set{}({});", instance, ::std::stringify!(#oneliners).to_case(Case::Pascal), self.#oneliners.reds_value()));
}
)*
let mut child_name;
let parent_name = if self.name.is_default() { None } else { Some(self.name.reds_value()) };
if self.child_order == inkEChildOrder::Forward {
for child in self.children.iter() {
child_name = child.name().expect("no child should be a inkMultiChildren");
steps.push(child.reds_widget(child_name, parent_name.as_ref().map(|x| x.as_str())));
}
for child in self.children.iter() {
child_name = child.name().expect("no child should be a inkMultiChildren");
steps.push(format!("{}.AddChildWidget({});", instance, child_name));
}
} else {
for child in self.children.iter().rev() {
child_name = child.name().expect("no child should be a inkMultiChildren");
steps.push(child.reds_widget(child_name, parent_name.as_ref().map(|x| x.as_str())));
}
for child in self.children.iter().rev() {
child_name = child.name().expect("no child should be a inkMultiChildren");
steps.push(format!("{}.AddChildWidget({});", instance, child_name));
}
}
steps.join("\n")
}
}
}.into()
}
9 changes: 9 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ pub struct PathNames {
pub path: std::vec::Vec<String>,
}

#[derive(clap::Args, Debug)]
pub struct OptionalPathNames {
/// filter by widget path name(s)
///
/// e.g. "main_canvas.Arrival.Arrival_GPS_Canvas.Arrival_GPS_Elements_Canvas"
#[arg(short, long, value_parser = parse_path_names, value_name = "NAMES")]
pub names: Option<std::vec::Vec<String>>,
}

#[derive(clap::Args, Debug)]
pub struct Mode {
/// optionally output as JSON, Redscript or table (default)
Expand Down
5 changes: 4 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::Parser;

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

#[allow(clippy::upper_case_acronyms)]
#[derive(Parser)] // requires `derive` feature
Expand All @@ -14,4 +14,7 @@ pub enum CLI {
/// get full path indexes from path names
#[command(name = "whereis")]
WhereIs(whereis::Args),
/// show a particular widget
#[command(name = "show")]
Show(show::Args),
}
8 changes: 4 additions & 4 deletions src/ink/anim/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod display;
use serde::{Deserialize, Serialize};
use serde_aux::prelude::*;

use crate::{HDRColor, Name, Vector2};
use crate::{CName, HDRColor, Vector2};

use super::InkWrapper;

Expand Down Expand Up @@ -124,8 +124,8 @@ pub struct Interpolator {
#[serde(rename_all = "camelCase")]
pub struct EffectInterpolator {
pub effect_type: inkEffectType,
pub effect_name: Name,
pub param_name: Name,
pub effect_name: CName,
pub param_name: CName,
#[serde(flatten)]
pub base: Interpolator,
}
Expand Down Expand Up @@ -307,7 +307,7 @@ pub struct InkAnimSequence {
///
/// ⚠️ `definitions` size must always match `targets` size
pub definitions: Vec<InkWrapper<InkAnimDefinition>>,
pub name: Name,
pub name: CName,
/// describe the targets onto which the interpolations are played
///
/// ⚠️ `targets` size must always match `definitions` size
Expand Down
104 changes: 104 additions & 0 deletions src/ink/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,110 @@ use serde::Deserialize;
use crate::anim::Range;
use crate::LocKey;

pub fn deserialize_cname_from_format<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: de::Deserializer<'de>,
{
struct CNameVisitor;
impl<'de> de::Visitor<'de> for CNameVisitor {
type Value = String;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("$type CName, with valid $storage and $value")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut type_ok = false;
let mut storage_ok = false;
let mut value_ok = false;
let mut value: String = String::new();
while let Some(key) = map.next_key::<&str>()? {
if key == "$type" {
let value: &str = map.next_value()?;
if value != "CName" {
return Err(de::Error::custom("invalid map type"));
} else {
type_ok = true;
}
}
if key == "$storage" {
let value: &str = map.next_value()?;
if value != "string" {
return Err(de::Error::custom("invalid map storage"));
} else {
storage_ok = true;
}
}
if key == "$value" {
value = map.next_value::<String>()?;
value_ok = true;
}
}
if type_ok && storage_ok && value_ok {
return Ok(value);
}
Err(de::Error::custom("invalid map sequence"))
}
}
deserializer.deserialize_any(CNameVisitor)
}

pub fn deserialize_resourcepath_from_format<'de, D>(
deserializer: D,
) -> Result<std::path::PathBuf, D::Error>
where
D: de::Deserializer<'de>,
{
struct ResourcePathVisitor;
impl<'de> de::Visitor<'de> for ResourcePathVisitor {
type Value = std::path::PathBuf;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("$type ResourcePath, with valid $storage and $value")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut type_ok = false;
let mut storage_ok = false;
let mut value_ok = false;
let mut value: String = String::new();
while let Some(key) = map.next_key::<&str>()? {
if key == "$type" {
let value: &str = map.next_value()?;
if value != "ResourcePath" {
return Err(de::Error::custom("invalid map type"));
} else {
type_ok = true;
}
}
if key == "$storage" {
let value: &str = map.next_value()?;
if value != "string" {
return Err(de::Error::custom("invalid map storage"));
} else {
storage_ok = true;
}
}
if key == "$value" {
value = map.next_value::<String>()?;
value_ok = true;
}
}
if type_ok && storage_ok && value_ok {
return Ok(std::path::PathBuf::from(value));
}
Err(de::Error::custom("invalid map sequence"))
}
}
deserializer.deserialize_any(ResourcePathVisitor)
}

pub fn deserialize_lockey_from_anything<'de, D>(deserializer: D) -> Result<Option<LocKey>, D::Error>
where
D: de::Deserializer<'de>,
Expand Down
Loading
Loading