context_mapper/
lib.rs

1//! Universal library for struct mapping. Provides multiple methods to
2//! generate HashMaps with values of any type
3//!
4//! # Example problem
5//!
6//! Let's assume that we've got this struct
7//! ```
8//! struct Person {
9//!     name: String
10//!     /// Age in years
11//!     age: usize,
12//!     /// Dept in $ in the certain banks
13//!     dept: BTreeMap<String, f64>,
14//!     /// Amount of money in banks. Accounts in $
15//!     bank_account: BTreeMap<String, f64>,
16//!     /// Amount of money in the wallet in $
17//!     wallet: f64,
18//!     /// Amount of money in the crypto in crypto-wallets. Transformed to $
19//!     crypto: BTreeMap<String, f64>
20//! }
21//! ```
22//!
23//! Now, we want to get a few info:
24//! 1. `avg(&self) -> HashMap<String, f64>` returning average amount of money in every field
25//! 2. `max(&self) -> HashMap<String, f64>` returning max amount of money in every field
26//! 3. `info(&self) -> HashMap<String, String>` returning general information about fields
27//! 4. `detail_info(&self) -> HashMap<String, String>`. Similar to 3, but with more info
28//!
29//! We would need to implement it manually. It's cumbersome and inconvinient. That's where
30//! `context_mapper` becomes a handy tool:
31//!
32//! ```
33//! #[derive(ContextMapper)]
34//! // F64 traits
35//! #[context_mapper(
36//!     trait(
37//!         context = p01::average,
38//!         type = f64,
39//!         converter = MyAvgTrait::avg,
40//!         generic(
41//!             name = Averager,
42//!             method_name = avg
43//!         )
44//!     ),
45//!     trait(
46//!         context = p02::max,
47//!         type = f64,
48//!         converter = MyMaxTrait::max,
49//!         simple(
50//!             name = Maximizer,
51//!             method_name = max
52//!         )
53//!     )
54//! )]
55//! // String traits
56//! #[context_mapper(
57//!     trait(
58//!         context = p03::general,
59//!         type = String,
60//!         converter = std::string::ToString::to_string,
61//!         generic(
62//!             path = GenInfo,
63//!             method_name = info
64//!         )
65//!     ),
66//!     trait(
67//!         context = p04::max,
68//!         type = f64,
69//!         converter = generic_info,
70//!         simple(
71//!             path = my::DetailedInfo,
72//!             method_name = info
73//!         )
74//!     )
75//! )]
76//! struct Person {
77//!     #[context_attribute(context(name=p01::average, skip))]
78//!     #[context_attribute(context(name=p02::max, skip))]
79//!     name: String
80//!
81//!     /// Age in years
82//!     #[context_attribute(context(name=p01::average, skip))]
83//!     #[context_attribute(context(name=p02::max, skip))]
84//!     age: usize,
85//!
86//!     /// Dept in $ in the certain banks
87//!     #[context_attribute(
88//!         context(name=p02::max, converter=punishment::with_interests)
89//!     )]
90//!     dept: BTreeMap<String, f64>,
91//!
92//!     /// Amount of money in banks. Accounts in $
93//!     bank_account: BTreeMap<String, f64>,
94//!     /// Amount of money in the wallet in $
95//!     wallet: f64,
96//!     /// Amount of money in the crypto in crypto-wallets. Transformed to $
97//!     crypto: BTreeMap<String, f64>
98//! }
99//! ```
100//!
101//! # Macros documentation
102//! See [`ContextMapper`] for detailed info
103//!
104//! # Comparsion to similar libraries
105//!
106//! ## serde
107//!
108//! The main difference between `context_mapper` and `serde` is the approach.
109//! `serde` defines single, powerful way of serializing / deserializing
110//! structures, enums etc. On the other hand `context_mapper` does not enforce
111//! any standard on the serialization. Its main purpose is to simplify generation of
112//! the repetitive struct -> map mappings. Also, it does not support nested structures
113//!
114//! ## structmap
115//!
116//! While being inspiration for this library, `structmap` is more similar 
117//! to serde, than `context_mapper`. Both libraries provide way of converision
118//! of the struct to map of strings. However `structmap` allows users to convert
119//! objects from map and then convert them to map. On the other hand it does not
120//! allow multiple contexts (multiple maps generation)
121//!
122pub use context_mapper_derive::ContextMapper;
123use std::collections::HashMap;
124
125pub struct Error(pub String);
126impl Error {
127    pub fn new<T: std::error::Error>(s: T) -> Self {
128        Self(s.to_string())
129    }
130}
131
132impl<T: std::error::Error> From<T> for Error {
133    fn from(value: T) -> Self {
134        Self(value.to_string())
135    }
136}
137
138pub trait IntoType<ContextType> {
139    fn into_type_map(&self) -> HashMap<String, ContextType>;
140}
141
142pub trait TryIntoType<ContextType> {
143    type Error;
144    fn try_into_type_map(&self) -> Result<HashMap<String, ContextType>, Self::Error>;
145}