This file is a merged representation of the entire codebase, combining all repository files into a single document.
Generated by Repomix on: 2025-02-27T08:56:26.166Z

================================================================
File Summary
================================================================

Purpose:
--------
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.

File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Multiple file entries, each consisting of:
  a. A separator line (================)
  b. The file path (File: path/to/file)
  c. Another separator line
  d. The full contents of the file
  e. A blank line

Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
  original repository files, not this packed version.
- When processing this file, use the file path to distinguish
  between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
  the same level of security as you would the original repository.

Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's
  configuration.
- Binary files are not included in this packed representation. Please refer to
  the Repository Structure section for a complete list of file paths, including
  binary files.

Additional Info:
----------------

================================================================
Directory Structure
================================================================
src/
  from_hashmap.rs
  lib.rs
tests/
  from_hashmap_test.rs
.gitignore
Cargo.toml
README.md

================================================================
Files
================================================================

================
File: src/from_hashmap.rs
================
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Type, TypePath};

pub(crate) fn derive_custom_model_impl(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let struct_name = &input.ident;

    let fields = if let Data::Struct(data) = &input.data {
        if let Fields::Named(fields) = &data.fields {
            fields
        } else {
            panic!("FromHashMap can only be derived on structs with named fields.");
        }
    } else {
        panic!("FromHashMap can only be derived on structs.");
    };

    let field_mappings = fields.named.iter().map(|f| {
        let field_name = &f.ident;
        let field_str = field_name.as_ref().unwrap().to_string();
        let ty = &f.ty;

        // Handle nested struct types
        if let Type::Path(TypePath { path, .. }) = ty {
            let type_name = quote!(#path).to_string();


            //TODO
            if !["String", "u8", "u16", "i32", "f64", "bool", "Option"]
                .iter()
                .any(|&t| type_name.contains(t))
            {
                return quote! {
                    #field_name: fields.iter()
                        .filter(|(k, _)| k.starts_with(&(#field_str.to_string() + ".")))
                        .map(|(k, v)| (k.trim_start_matches(&(#field_str.to_string() + ".")).to_string(), v.clone()))
                        .collect::<std::collections::HashMap<String, String>>()
                        .into(),
                };
            }
        }

        // Handle specific primitive types
        if quote!(#ty).to_string().contains("Option") {
            quote! {
                #field_name: fields.get(#field_str).cloned(),
            }
        } else if quote!(#ty).to_string().contains("u8") {
            quote! {
                #field_name: fields.get(#field_str)
                    .and_then(|val| val.parse::<u8>().ok())
                    .unwrap_or_default(),
            }
        } else if quote!(#ty).to_string().contains("i32") {
            quote! {
                #field_name: fields.get(#field_str)
                    .and_then(|val| val.parse::<i32>().ok())
                    .unwrap_or_default(),
            }
        } else if quote!(#ty).to_string().contains("f64") {
            quote! {
                #field_name: fields.get(#field_str)
                    .and_then(|val| val.parse::<f64>().ok())
                    .unwrap_or_default(),
            }
        } else {
            quote! {
                #field_name: fields.get(#field_str).cloned().unwrap_or_default(),
            }
        }
    });

    let expanded = quote! {
        impl From<std::collections::HashMap<String, String>> for #struct_name {
            fn from(fields: std::collections::HashMap<String, String>) -> Self {
                Self {
                    #(#field_mappings)*
                }
            }
        }
    };

    TokenStream::from(expanded)
}

================
File: src/lib.rs
================
extern crate proc_macro;

use proc_macro::TokenStream;

mod from_hashmap;

#[proc_macro_derive(FromHashMap)]
pub fn from_hashmap_derive(input: TokenStream) -> TokenStream {
    from_hashmap::derive_custom_model_impl(input).into()
}

================
File: tests/from_hashmap_test.rs
================
use std::collections::HashMap;
use doless::FromHashMap;

#[derive(FromHashMap, Debug)]
struct Car {
    model: String,
    brand: String,
    an_option_field: Option<String>,
    number: u8,
    details: CarDetails,
}

#[derive(FromHashMap, Debug)]
struct CarDetails {
    name: String,
    description: String,
}

#[test]
fn test_from_hashmap() {
    let mut data = HashMap::new();
    data.insert("model".to_string(), "GT-R".to_string());
    data.insert("brand".to_string(), "Nissan".to_string());
    data.insert("number".to_string(), "8".to_string());
    data.insert("details.name".to_string(), "v8engine".to_string());
    data.insert("details.description".to_string(), "500hp".to_string());

    let car: Car = Car::from(data);

    assert_eq!(car.model, "GT-R");
    assert_eq!(car.brand, "Nissan");
    assert_eq!(car.number, 0b1000);
    assert_eq!(car.an_option_field, None);
    assert_eq!(car.details.name, "v8engine");
    assert_eq!(car.details.description, "500hp");
}

================
File: .gitignore
================
debug/
target/
Cargo.lock
**/*.rs.bk
*.pdb

================
File: Cargo.toml
================
[package]
name = "doless"
version = "0.1.0"
edition = "2021"
description = "A Rust macro to simplify struct mapping and function utilities."
license = "MIT OR Apache-2.0"
repository = "https://github.com/your-username/doless"
keywords = ["macro", "procedural", "struct", "mapping", "timing"]
categories = ["development-tools", "rust-patterns"]
readme = "README.md"

[dependencies]
syn = { version = "2", features = ["derive", "parsing"] }
quote = "1"
proc-macro2 = "1"



[lib]
proc-macro = true

================
File: README.md
================
# DoLess - Procedural Macro for Struct Mapping 🦀

`DoLess` is a Rust **procedural macro** that allows structs to be initialized from a `HashMap<String, String>`. It automatically maps field values, providing **type-safe conversions**.

## 🚀 Features
- 🏢 **Auto-implements `From<HashMap<String, String>>`** for structs.
- 🔄 **Supports common Rust types** (`String`, `u8`, `u16`, `i32`, `f64`, `Option`, etc.).
- ❌ **Compile-time errors for unsupported types**.
- ✅ **Default values for missing fields**.
- ⚙ **Supports nested struct parsing** with `.` notation.

---

## 📦 Installation
Add `DoLess` to your `Cargo.toml`:

```toml
[dependencies]
doless = "0.1.0"
```

## 👺 Usage

### Basic Struct Mapping
```rust
use doless::FromHashMap;
use std::collections::HashMap;

#[derive(FromHashMap, Debug, PartialEq)]
struct Car {
    model: String,
    year: u16,
}

fn main() {
    let mut data = HashMap::new();
    data.insert("model".to_string(), "GT-R".to_string());
    data.insert("year".to_string(), "2023".to_string());

    let car: Car = Car::from(data);
    println!("Car: Model = {}, Year = {}", car.model, car.year);
}
```

### Nested Struct Support

```rust
use doless::FromHashMap;
use std::collections::HashMap;

#[derive(FromHashMap, Debug)]
struct Car {
    model: String,
    brand: String,
    number: u8,
    details: CarDetails,  // ✅ Nested Struct Support
}

#[derive(FromHashMap, Debug)]
struct CarDetails {
    name: String,
    description: String,
}

fn main() {
    let mut data = HashMap::new();
    data.insert("model".to_string(), "GT-R".to_string());
    data.insert("brand".to_string(), "Nissan".to_string());
    data.insert("number".to_string(), "8".to_string());

    // ✅ Nested Fields with Prefix Notation
    data.insert("details.name".to_string(), "Skyline".to_string());
    data.insert("details.description".to_string(), "Legendary Sports Car".to_string());

    let car: Car = Car::from(data);
    println!("{:?}", car);
}
```

### Expected Output
```rust
Car {
    model: "GT-R",
    brand: "Nissan",
    number: 8,
    details: CarDetails {
        name: "Skyline",
        description: "Legendary Sports Car"
    }
}
```

---

## 🚀 Why Use DoLess?
- **Simple & Lightweight** — No runtime dependencies, just pure Rust.
- **Declarative API** — Uses procedural macros to generate efficient `From<HashMap<String, String>>` implementations.
- **Type-Safe & Extensible** — Ensures correct conversions and supports nesting.

### ⚙ Roadmap
- [x] Basic primitive types mapping
- [x] Nested struct support
- [ ] Custom conversion support
- [ ] Error handling improvements

---

**Happy coding! ✨**
