Skip to content

Commit 92f1fa2

Browse files
authored
Merge pull request #1 from akashsoni01/main
v0.2.0
2 parents 8147f36 + 7795971 commit 92f1fa2

File tree

13 files changed

+841
-2
lines changed

13 files changed

+841
-2
lines changed

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,14 @@ target
1919
# and can be added to the global gitignore or merged into this file. For a more nuclear
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
2121
#.idea/
22+
23+
24+
# Added by cargo
25+
26+
/target
27+
28+
/Cargo.lock
29+
/.idea
30+
key-paths-core/.idea/vcs.xml
31+
key-paths-core/.idea/tagged-core.iml
32+
key-paths-core/.idea/modules.xml

Cargo.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "rust-key-paths"
3+
version = "0.2.0"
4+
edition = "2024"
5+
authors = ["Codefonsi <[email protected]>"]
6+
license = "MPL-2.0"
7+
description = "ReadableKeyPath, WritableKeyPath and EnumKeypath for struct and enums in Rust."
8+
repository = "https://github.com/codefonsi/rust-key-paths"
9+
homepage = "https://github.com/codefonsi/rust-key-paths"
10+
documentation = "https://docs.rs/rust-key-paths"
11+
keywords = ["keypaths", "EnumKeyPath", "type-safe", "WritableKeyPath", "ReadableKeyPath"]
12+
readme = "./README.md"
13+
include = ["src/**/*", "Cargo.toml", "../../README.md", "LICENSE"]
14+
15+
[dependencies]
16+
key-paths-core = { path = "key-paths-core", version = "0.1.0" }
17+
18+
19+
[workspace]
20+
resolver = "3" # or "3"
21+
members = [
22+
"key-paths-core"
23+
]
24+
25+
[patch.crates-io]
26+
key-paths-core = { path = "key-paths-core" }
27+
28+
29+
[features]
30+
default = []

README.md

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,172 @@
1-
# rust-key-paths
2-
ReadableKeyPath and WritableKeyPath for struct and enums in Rust
1+
# 🔑 KeyPaths & CasePaths in Rust
2+
3+
Key paths and case paths provide a **safe, composable way to access and modify nested data** in Rust.
4+
Inspired by **Swift’s KeyPath / CasePath** system, this crate lets you work with **struct fields** and **enum variants** as *first-class values*.
5+
6+
---
7+
8+
## ✨ Features
9+
10+
***ReadableKeyPath** → safely read struct fields.
11+
***WritableKeyPath** → safely read/write struct fields.
12+
***EnumKeyPath (CasePaths)** → extract and embed enum variants.
13+
***Composable** → chain key paths together(Upcoming).
14+
***Iterable** → iterate or mutate values across collections.
15+
***Macros** → concise `readable_keypath!`, `writable_keypath!`, `enum_keypath!`.
16+
17+
---
18+
19+
## 📦 Installation
20+
21+
```toml
22+
[dependencies]
23+
key_paths_core = "0.1"
24+
```
25+
26+
---
27+
28+
## 🚀 Examples
29+
30+
### 1. CasePaths with Enums
31+
32+
```rust
33+
use key_paths_core::enum_keypath;
34+
use key_paths_core::EnumKeyPath;
35+
36+
#[derive(Debug)]
37+
struct User {
38+
id: u32,
39+
name: String,
40+
}
41+
42+
#[derive(Debug)]
43+
enum Status {
44+
Active(User),
45+
Inactive(()),
46+
}
47+
48+
fn main() {
49+
let cp = enum_keypath!(Status::Active(User));
50+
51+
let status = Status::Active(User {
52+
id: 42,
53+
name: "Charlie".to_string(),
54+
});
55+
56+
if let Some(u) = cp.extract(&status) {
57+
println!("Extracted user: {:?}", u);
58+
}
59+
60+
let new_status = cp.embed(User {
61+
id: 99,
62+
name: "Diana".to_string(),
63+
});
64+
println!("Embedded back: {:?}", new_status);
65+
}
66+
```
67+
68+
---
69+
70+
### 2. Readable KeyPaths
71+
72+
```rust
73+
use key_paths_core::Readable;
74+
use key_paths_core::ReadableKeyPath;
75+
use key_paths_core::readable_keypath;
76+
77+
#[derive(Debug)]
78+
struct User {
79+
name: String,
80+
age: u32,
81+
}
82+
83+
fn main() {
84+
let users = vec![
85+
User { name: "Akash".into(), age: 25 },
86+
User { name: "Soni".into(), age: 30 },
87+
User { name: "Neha".into(), age: 20 },
88+
];
89+
90+
let name_key = readable_keypath!(User, name);
91+
92+
println!("Names:");
93+
for name in name_key.iter(&users) {
94+
println!("{}", name);
95+
}
96+
}
97+
```
98+
99+
---
100+
101+
### 3. Writable KeyPaths
102+
103+
```rust
104+
use key_paths_core::writable_keypath;
105+
use key_paths_core::WritableKeyPath;
106+
use key_paths_core::Readable;
107+
use key_paths_core::Writable;
108+
109+
#[derive(Debug)]
110+
struct User {
111+
name: String,
112+
age: u32,
113+
}
114+
115+
fn main() {
116+
let mut users = vec![
117+
User { name: "Akash".into(), age: 25 },
118+
User { name: "Soni".into(), age: 30 },
119+
User { name: "Neha".into(), age: 20 },
120+
];
121+
122+
let age_key = writable_keypath!(User, age);
123+
124+
println!("Ages before:");
125+
for age in age_key.iter(&users) {
126+
println!("{}", age);
127+
}
128+
129+
for age in age_key.iter_mut(&mut users) {
130+
*age += 1;
131+
}
132+
133+
println!("Ages after:");
134+
for age in age_key.iter(&users) {
135+
println!("{}", age);
136+
}
137+
}
138+
```
139+
140+
---
141+
142+
## 🔗 Helpful Links & Resources
143+
144+
* 📘 [Swift KeyPath documentation](https://developer.apple.com/documentation/swift/keypath)
145+
* 📘 [Swift CasePath library (pointfreeco)](https://github.com/pointfreeco/swift-case-paths)
146+
* 📘 [Elm Architecture & Functional Lenses](https://guide.elm-lang.org/architecture/)
147+
* 📘 [Rust Macros Book](https://doc.rust-lang.org/book/ch19-06-macros.html)
148+
* 📘 [Category Theory in FP (for intuition)](https://bartoszmilewski.com/2014/11/24/category-the-essence-of-composition/)
149+
150+
---
151+
152+
## 💡 Why use KeyPaths?
153+
154+
* Avoids repetitive `match` / `.` chains.
155+
* Encourages **compositional design**.
156+
* Plays well with **DDD (Domain-Driven Design)** and **Actor-based systems**.
157+
* Useful for **reflection-like behaviors** in Rust (without unsafe).
158+
159+
---
160+
161+
## 🛠 Roadmap
162+
163+
* [ ] `zip` support for combining multiple key paths (Upcoming).
164+
* [ ] Derive macros for automatic KeyPath generation.
165+
* [ ] Nested struct & enum traversal.
166+
* [ ] Optional chaining (`User?.profile?.name`).
167+
168+
---
169+
170+
## 📜 License
171+
172+
* Mozilla Public License 2.0

examples/enum_keypath_example.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use key_paths_core::enum_keypath;
2+
3+
#[derive(Debug)]
4+
struct User {
5+
id: u32,
6+
name: String,
7+
}
8+
9+
#[derive(Debug)]
10+
enum Status {
11+
Active(User),
12+
Inactive(()),
13+
}
14+
15+
#[derive(Debug)]
16+
enum SomeOtherStatus {
17+
Active(String),
18+
Inactive,
19+
}
20+
21+
22+
fn main() {
23+
// ---------- EnumPath ----------
24+
let cp = enum_keypath!(Status::Active(User));
25+
let cp2 = enum_keypath!(Status::Inactive(()));
26+
27+
28+
let cp3 = enum_keypath!(SomeOtherStatus::Active(String));
29+
if let Some(x) = cp3.extract(&SomeOtherStatus::Active("Hello".to_string())) {
30+
println!("Active: {:?}", x);
31+
}
32+
33+
let cp4 = enum_keypath!(SomeOtherStatus::Inactive);
34+
if let Some(x) = cp4.extract(&SomeOtherStatus::Inactive) {
35+
println!("Inactive: {:?}", x);
36+
}
37+
38+
let status = Status::Active(User {
39+
id: 42,
40+
name: "Charlie".to_string(),
41+
});
42+
43+
if let Some(u) = cp.extract(&status) {
44+
println!("Extracted user: {:?}", u);
45+
}
46+
47+
let new_status = cp.embed(User {
48+
id: 99,
49+
name: "Diana".to_string(),
50+
});
51+
println!("Embedded back: {:?}", new_status);
52+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use key_paths_core::Readable;
2+
use key_paths_core::ReadableKeyPath;
3+
use key_paths_core::readable_keypath;
4+
5+
#[derive(Debug)]
6+
struct User {
7+
name: String,
8+
age: u32,
9+
}
10+
11+
fn main() {
12+
let users = vec![
13+
User { name: "Akash".into(), age: 25 },
14+
User { name: "Soni".into(), age: 30 },
15+
User { name: "Neha".into(), age: 20 },
16+
];
17+
18+
19+
// Read-only keypath
20+
// let name_key = ReadableKeyPath::new(|u: &User| &u.name);
21+
let name_key = readable_keypath!(User, name);
22+
23+
// Writable keypath
24+
// let age_key = WritableKeyPath::new(
25+
// |u: &User| &u.age,
26+
// |u: &mut User| &mut u.age,
27+
// );
28+
// let age_key = writable_keypath!(User, age);
29+
30+
31+
println!("Names:");
32+
for name in name_key.iter(&users) {
33+
println!("{}", name);
34+
}
35+
36+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use key_paths_core::writable_keypath;
2+
use key_paths_core::WritableKeyPath;
3+
use key_paths_core::Readable;
4+
use key_paths_core::Writable;
5+
6+
#[derive(Debug)]
7+
struct User {
8+
name: String,
9+
age: u32,
10+
}
11+
12+
fn main() {
13+
let mut users = vec![
14+
User { name: "Akash".into(), age: 25 },
15+
User { name: "Soni".into(), age: 30 },
16+
User { name: "Neha".into(), age: 20 },
17+
];
18+
19+
20+
// Read-only keypath
21+
// let name_key = ReadableKeyPath::new(|u: &User| &u.name);
22+
// let name_key = readable_keypath!(User, name);
23+
24+
// Writable keypath
25+
// let age_key = WritableKeyPath::new(
26+
// |u: &User| &u.age,
27+
// |u: &mut User| &mut u.age,
28+
// );
29+
let age_key = writable_keypath!(User, age);
30+
31+
32+
// println!("Names:");
33+
// for name in name_key.iter(&users) {
34+
// println!("{}", name);
35+
// }
36+
37+
println!("Ages before:");
38+
for age in age_key.iter(&users) {
39+
println!("{}", age);
40+
}
41+
42+
// Mutate agesiter
43+
for age in age_key.iter_mut(&mut users) {
44+
*age += 1;
45+
}
46+
47+
println!("Ages after:");
48+
for age in age_key.iter(&users) {
49+
println!("{}", age);
50+
}
51+
52+
53+
54+
}

key-paths-core/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "key-paths-core"
3+
version = "0.1.0"
4+
edition = "2024"
5+
authors = ["Codefonsi <[email protected]>"]
6+
license = "MPL-2.0"
7+
description = "ReadableKeyPath, WritableKeyPath and EnumKeypath for struct and enums in Rust."
8+
repository = "https://github.com/codefonsi/rust-key-paths"
9+
homepage = "https://github.com/codefonsi/rust-key-paths"
10+
documentation = "https://docs.rs/rust-key-paths"
11+
keywords = ["keypaths", "EnumKeyPath", "type-safe", "WritableKeyPath", "ReadableKeyPath"]
12+
readme = "./README.md"
13+
include = ["src/**/*", "Cargo.toml", "../../README.md", "LICENSE"]
14+
15+
[dependencies]

0 commit comments

Comments
 (0)