Coder Social home page Coder Social logo

Comments (20)

Amanse avatar Amanse commented on August 11, 2024

i made a similar workflow for my game-rs recently
uses a library to make a simple toml config and a config command which changes config as per needs with FuzzySelector menu
https://github.com/amanse/game-rs

i can make a quick pr, what will be the options in the config?

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

That would be wonderful, can you please wait for me to add the config first?
You can exclusively work on the command after that.

Note:

- toml
+ yaml

since it is more user friendly

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

yess, I'll be waiting
also if i could suggest something, you can use confy, it supports toml, yaml and json and handles the config part with no boilerplate and for all OSes

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

I have already written most of the boilerplate code either way, so its ok

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

@Amanse here is your plalyground :)

Go wild, I left some comments on how I want this to proceed, but I am open to suggestions.

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

One more request that I have is that before applying any change to the config, show a diff and prompt the user if they want to proceed.

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

nicee that's a great idea, I'll get right on it

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

Hey, did you have something specific in mind to iterate over the struct fields?
rust doesn't make it easy to dynamically update a field in a struct and serde(_aux) only gives name of field. making static update function for each field will be dumb

i can think of 2 ways it can go:

  • either to Deserialize into a hashmap so we can change with key (selected by the user) [sub-fields will be hashmap inside hasmap]
  • or use something like bevy_reflect which allows changing value with dynamic values but takes away some of the memory safety guarantees of rust

do you have any preference on which way to go or any other way you can think of?

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

rust doesn't make it easy to dynamically update a field in a struct and serde(_aux) only gives name of field. making static update function for each field will be dumb

I'll try and make a small prototype for that

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

yeah bevy_reflect looks like the way to go, or writing a trait and implementing it for every single structure in the config

actually, the latter sounds more safe and easy, ig ill take the L

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

yes, the latter is immensely more easy as per my limited experience with bevy_reflect

another way i can think is, since we can get an index and field name with serde safely, we can make a selection to enum mapper and make a function which takes a value and enum and just updates with match
it's the easiest option by far and updating this when config grows will be minimal
but will need a long ass match

trait will be prettier way to code doe, I'll give it a shot next

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

Ayo check this out
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=06f2842006c70215d1c4a81f780236dd

macros can change dynamically, when we add anything to config we just have to add a macro line, it can even handle different prompts for different types

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

Took me some time to learn procedural macros, btw what you linked is a declarative macro

Here I made an example of how this could work

module fake_reflect

Cargo.toml

Toggle
[package]
name = "fake_reflect"
version = "0.1.0"
edition = "2018"

[lib]
proc-macro = true

[dependencies]
syn = "0.15"
quote = "0.6"

lib.rs

Toggle
extern crate proc_macro;
extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use syn::{Data, DataStruct, DeriveInput, Fields, Ident};

fn impl_hello_world(ast: &DeriveInput) -> TokenStream {
    let name = &ast.ident;

    let fields = match &ast.data {
        Data::Struct(DataStruct {
            fields: Fields::Named(fields),
            ..
        }) => &fields.named,
        _ => panic!("expected a struct with named fields"),
    };

    let field_name = fields.iter().map(|field| &field.ident);
    let field_name2 = field_name.clone();
    let field_name3 = field_name.clone();

    let field_type = fields.iter().map(|field| &field.ty);

    let enum_name = syn::parse_str::<Ident>(&format!("{}_field", name.to_string())).unwrap();

    let enum_vars = fields.iter().map(|field| {
        syn::parse_str::<syn::Path>(&format!("{}::{}", enum_name, field.ident.as_ref().unwrap()))
            .unwrap()
    });

    TokenStream::from(quote! {
        #ast

        enum #enum_name {

            #(
                #field_name(#field_type),
            )*
        }

        impl #name {
            fn get(&self, field: impl ToString) -> anyhow::Result<#enum_name> {
                match field.to_string().as_str() {
                    #(
                        stringify!(#field_name2) => Ok(#enum_vars(self.#field_name3)),
                    )*
                    _ => Err(anyhow::anyhow!("Unknown field.")),
                }
            }
        }
    })
}

#[proc_macro_attribute]
pub fn hello_world(args: TokenStream, input: TokenStream) -> TokenStream {
    let _ = args;

    // Parse the string representation
    let ast = syn::parse_macro_input!(input as DeriveInput);

    // Build and return the generated impl
    impl_hello_world(&ast)
}

usage

Cargo.toml

Toggle
...

[dependencies]
fake_reflect = { path = "fake_reflect" }

...

main.rs

Toggle
#[macro_use]
extern crate fake_reflect;

pub fn main() {
    #[fake_reflect::hello_world]
    struct Waffles {
        hello: i16,
    }

    let a = Waffles { hello: 42 };

    match a.get("hello").unwrap() {
        Waffles_field::hello(value) => println!("{}", value),
        _ => (),
    };
}

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

Here is the generated code from the above message:

pub fn main() {
    struct Waffles {
        hello: i16,
    }
    enum Waffles_field {
        hello(i16),
    }
    impl Waffles {
        fn get(&self, field: impl ToString) -> anyhow::Result<Waffles_field> {
            match field.to_string().as_str() {
                "hello" => Ok(Waffles_field::hello(self.hello)),
                _ => Err(anyhow::anyhow!("Unknown field.")),
            }
        }
    }
    let a = Waffles { hello: 42 };
    match a.get("hello").unwrap() {
        Waffles_field::hello(value) => println!("{}", value),
        _ => ()
    };
}

PS: I used cargo expand

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

wow, took me some time to understand but this simplifies the whole process, rust macros are awesome lol
will make a pr asap

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

kinda under estimated rust borrow checker, String doesn't implement Copy strikes again, it may take some more time than i expected

from anime-dl.

ArjixWasTaken avatar ArjixWasTaken commented on August 11, 2024

kinda under estimated rust borrow checker, String doesn't implement Copy strikes again, it may take some more time than i expected

.clone()

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

i misunderstood what the error meant, working now, partially added the update method too

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

Update function working
Only need to loop over fields and call the function for selected field now

from anime-dl.

Amanse avatar Amanse commented on August 11, 2024

sorry i was in hospital for a few days
What i am stuck on is getting new value into proc macro, since field type needs to be iterated
and another method that is working is passing value with field enum that we get from the get method. But we cannot change value inside enum from original to new value without matching with each enum

i was looking into ways of doing it with serde and found this

use-serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Serialize, Deserialize)]
struct ExampleStruct {
    field_a: i32,
    field_b: String,
}

fn main() {
    let mut my_struct = ExampleStruct {
        field_a: 42,
        field_b: String::from("hello"),
    };
    
    let mut field_updates = HashMap::new();
    field_updates.insert("field_a", 99);
    field_updates.insert("field_b", String::from("world"));
    
    for (field_name, field_value) in field_updates {
        let field_name = field_name.to_string();
        let field_value = serde_json::to_value(&field_value).unwrap();
        let mut fields = my_struct.serialize().unwrap();
        fields.remove(&field_name);
        fields.insert(field_name, field_value);
        my_struct = ExampleStruct::deserialize(fields).unwrap();
    }
    
    println!("Updated struct: {:?}", my_struct);
}

bevy_reflect seems a viable option too

from anime-dl.

Related Issues (5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.