Skip to content

Commit e19440e

Browse files
authored
Merge pull request #3 from akashsoni01/main
stable 0.4.0
2 parents 92f1fa2 + 2f5b6e0 commit e19440e

16 files changed

+517
-407
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ target
3030
key-paths-core/.idea/vcs.xml
3131
key-paths-core/.idea/tagged-core.iml
3232
key-paths-core/.idea/modules.xml
33+
key-paths-core/.idea/key-paths-core.iml

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rust-key-paths"
3-
version = "0.2.0"
3+
version = "0.4.0"
44
edition = "2024"
55
authors = ["Codefonsi <[email protected]>"]
66
license = "MPL-2.0"
@@ -13,7 +13,7 @@ readme = "./README.md"
1313
include = ["src/**/*", "Cargo.toml", "../../README.md", "LICENSE"]
1414

1515
[dependencies]
16-
key-paths-core = { path = "key-paths-core", version = "0.1.0" }
16+
key-paths-core = { path = "key-paths-core", version = "0.4.0" }
1717

1818

1919
[workspace]

README.md

Lines changed: 143 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Inspired by **Swift’s KeyPath / CasePath** system, this crate lets you work wi
2020

2121
```toml
2222
[dependencies]
23-
key_paths_core = "0.1"
23+
key_paths_core = "0.3"
2424
```
2525

2626
---
@@ -70,9 +70,7 @@ fn main() {
7070
### 2. Readable KeyPaths
7171

7272
```rust
73-
use key_paths_core::Readable;
74-
use key_paths_core::ReadableKeyPath;
75-
use key_paths_core::readable_keypath;
73+
use key_paths_core::{readable_keypath, ReadableKeyPath};
7674

7775
#[derive(Debug)]
7876
struct User {
@@ -82,13 +80,31 @@ struct User {
8280

8381
fn main() {
8482
let users = vec![
85-
User { name: "Akash".into(), age: 25 },
86-
User { name: "Soni".into(), age: 30 },
87-
User { name: "Neha".into(), age: 20 },
83+
User {
84+
name: "Akash".into(),
85+
age: 25,
86+
},
87+
User {
88+
name: "Soni".into(),
89+
age: 30,
90+
},
91+
User {
92+
name: "Neha".into(),
93+
age: 20,
94+
},
8895
];
8996

97+
// Read-only keypath
98+
// let name_key = ReadableKeyPath::new(|u: &User| &u.name);
9099
let name_key = readable_keypath!(User, name);
91100

101+
// Writable keypath
102+
// let age_key = WritableKeyPath::new(
103+
// |u: &User| &u.age,
104+
// |u: &mut User| &mut u.age,
105+
// );
106+
// let age_key = writable_keypath!(User, age);
107+
92108
println!("Names:");
93109
for name in name_key.iter(&users) {
94110
println!("{}", name);
@@ -101,10 +117,7 @@ fn main() {
101117
### 3. Writable KeyPaths
102118

103119
```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;
120+
use key_paths_core::{writable_keypath, WritableKeyPath};
108121

109122
#[derive(Debug)]
110123
struct User {
@@ -114,29 +127,142 @@ struct User {
114127

115128
fn main() {
116129
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 },
130+
User {
131+
name: "Akash".into(),
132+
age: 25,
133+
},
134+
User {
135+
name: "Soni".into(),
136+
age: 30,
137+
},
138+
User {
139+
name: "Neha".into(),
140+
age: 20,
141+
},
120142
];
121143

144+
// Read-only keypath
145+
// let name_key = ReadableKeyPath::new(|u: &User| &u.name);
146+
// let name_key = readable_keypath!(User, name);
147+
148+
// Writable keypath
149+
// let age_key = WritableKeyPath::new(
150+
// |u: & User| & u.age,
151+
// |u: &mut User| &mut u.age,
152+
// );
122153
let age_key = writable_keypath!(User, age);
123154

155+
// println!("Names:");
156+
// for name in name_key.iter(&users) {
157+
// println!("{}", name);
158+
// }
159+
124160
println!("Ages before:");
125161
for age in age_key.iter(&users) {
126162
println!("{}", age);
127163
}
128164

165+
// Mutate agesiter
129166
for age in age_key.iter_mut(&mut users) {
130167
*age += 1;
131168
}
132169

133170
println!("Ages after:");
134-
for age in age_key.iter(&users) {
171+
for age in age_key.iter(&mut users) {
135172
println!("{}", age);
136173
}
137174
}
138175
```
139176

177+
### 4. Composability and failablity
178+
```rust
179+
use key_paths_core::{FailableReadableKeyPath};
180+
181+
#[derive(Debug)]
182+
struct Engine {
183+
horsepower: u32,
184+
}
185+
#[derive(Debug)]
186+
struct Car {
187+
engine: Option<Engine>,
188+
}
189+
#[derive(Debug)]
190+
struct Garage {
191+
car: Option<Car>,
192+
}
193+
194+
fn main() {
195+
let garage = Garage {
196+
car: Some(Car {
197+
engine: Some(Engine { horsepower: 120 }),
198+
}),
199+
};
200+
201+
let kp_car = FailableReadableKeyPath::new(|g: &Garage| g.car.as_ref());
202+
let kp_engine = FailableReadableKeyPath::new(|c: &Car| c.engine.as_ref());
203+
let kp_hp = FailableReadableKeyPath::new(|e: &Engine| Some(&e.horsepower));
204+
205+
// Compose: Garage -> Car -> Engine -> horsepower
206+
let kp = kp_car.compose(kp_engine).compose(kp_hp);
207+
208+
let kp2 = FailableReadableKeyPath::new(|g: &Garage| {
209+
g.car
210+
.as_ref()
211+
.and_then(|c| c.engine.as_ref())
212+
.and_then(|e| Some(&e.horsepower))
213+
});
214+
215+
if let Some(hp) = kp.try_get(&garage) {
216+
println!("{hp:?}");
217+
}
218+
219+
if let Some(hp) = kp2.try_get(&garage) {
220+
println!("{hp:?}");
221+
}
222+
223+
println!("{garage:?}");
224+
}
225+
```
226+
### 4. Mutablity
227+
```rust
228+
use key_paths_core::{FailableWritableKeyPath};
229+
230+
#[derive(Debug)]
231+
struct Engine {
232+
horsepower: u32,
233+
}
234+
#[derive(Debug)]
235+
struct Car {
236+
engine: Option<Engine>,
237+
}
238+
#[derive(Debug)]
239+
struct Garage {
240+
car: Option<Car>,
241+
}
242+
243+
fn main() {
244+
let mut garage = Garage {
245+
car: Some(Car {
246+
engine: Some(Engine { horsepower: 120 }),
247+
}),
248+
};
249+
250+
let kp_car = FailableWritableKeyPath::new(|g: &Garage| g.car.as_ref(), |g: &mut Garage| g.car.as_mut());
251+
let kp_engine = FailableWritableKeyPath::new(|c: &Car| c.engine.as_ref(), |c: &mut Car| c.engine.as_mut());
252+
let kp_hp = FailableWritableKeyPath::new(|e: &Engine| Some(&e.horsepower), |e: &mut Engine| Some(&mut e.horsepower));
253+
254+
// Compose: Garage -> Car -> Engine -> horsepower
255+
let kp = kp_car.compose(kp_engine).compose(kp_hp);
256+
257+
println!("{garage:?}");
258+
if let Some(hp) = kp.try_get_mut(&mut garage) {
259+
*hp = 200;
260+
}
261+
262+
println!("{garage:?}");
263+
}
264+
```
265+
140266
---
141267

142268
## 🔗 Helpful Links & Resources
@@ -160,8 +286,8 @@ fn main() {
160286

161287
## 🛠 Roadmap
162288

163-
* [ ] `zip` support for combining multiple key paths (Upcoming).
164-
* [ ] Derive macros for automatic KeyPath generation.
289+
* [ ] `compose` support for combining multiple key paths.
290+
* [ ] Derive macros for automatic KeyPath generation (Upcoming).
165291
* [ ] Nested struct & enum traversal.
166292
* [ ] Optional chaining (`User?.profile?.name`).
167293

examples/basics.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use key_paths_core::*;
2+
3+
#[derive(Debug)]
4+
struct Size {
5+
width: u32,
6+
height: u32,
7+
}
8+
9+
#[derive(Debug)]
10+
struct Rectangle {
11+
size: Size,
12+
name: String,
13+
}
14+
15+
fn main() {
16+
let mut rect = Rectangle {
17+
size: Size {
18+
width: 30,
19+
height: 50,
20+
},
21+
name: "MyRect".into(),
22+
};
23+
24+
// Define readable and writable keypaths.
25+
let size_kp: ReadableKeyPath<Rectangle, Size> = ReadableKeyPath::new(|r: &Rectangle| &r.size);
26+
let width_kp: ReadableKeyPath<Size, u32> = ReadableKeyPath::new(|s: &Size| &s.width);
27+
28+
// Compose nested paths (assuming composition is supported).
29+
// e.g., rect[&size_kp.then(&width_kp)] — hypothetical chaining
30+
31+
// Alternatively, define them directly:
32+
let width_direct: ReadableKeyPath<Rectangle, u32> =
33+
ReadableKeyPath::new(|r: &Rectangle| &r.size.width);
34+
println!("Width: {}", width_direct.get(&rect));
35+
36+
// Writable keypath for modifying fields:
37+
let width_mut: WritableKeyPath<Rectangle, u32> = WritableKeyPath::new(
38+
|r: &Rectangle| &r.size.width,
39+
|r: &mut Rectangle| &mut r.size.width,
40+
);
41+
*(width_mut.get_mut)(&mut rect) = 100;
42+
println!("Updated rectangle: {:?}", rect);
43+
}

examples/compose.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use key_paths_core::FailableReadableKeyPath;
2+
3+
#[derive(Debug)]
4+
struct Engine {
5+
horsepower: u32,
6+
}
7+
8+
#[derive(Debug)]
9+
struct Car {
10+
engine: Option<Engine>,
11+
}
12+
13+
#[derive(Debug)]
14+
struct Garage {
15+
car: Option<Car>,
16+
}
17+
18+
#[derive(Debug)]
19+
struct City {
20+
garage: Option<Garage>,
21+
}
22+
23+
fn main() {
24+
let city = City {
25+
garage: Some(Garage {
26+
car: Some(Car {
27+
engine: Some(Engine { horsepower: 250 }),
28+
}),
29+
}),
30+
};
31+
32+
let city_hp2 = FailableReadableKeyPath::new(|c: &City| {
33+
c.garage
34+
.as_ref()
35+
.and_then(|g| g.car.as_ref())
36+
.and_then(|car| car.engine.as_ref())
37+
.and_then(|e| Some(&e.horsepower)) // ✅ removed the extra Some(...)
38+
});
39+
40+
println!("Horsepower = {:?}", (city_hp2.get)(&city));
41+
42+
// compose example ----
43+
// compose keypath together
44+
45+
let city_garage = FailableReadableKeyPath::new(|c: &City| c.garage.as_ref());
46+
let garage_car = FailableReadableKeyPath::new(|g: &Garage| g.car.as_ref());
47+
let car_engine = FailableReadableKeyPath::new(|c: &Car| c.engine.as_ref());
48+
let engine_hp = FailableReadableKeyPath::new(|e: &Engine| Some(&e.horsepower));
49+
50+
let city_hp = city_garage
51+
.compose(garage_car)
52+
.compose(car_engine)
53+
.compose(engine_hp);
54+
55+
println!("Horsepower = {:?}", (city_hp.get)(&city));
56+
}

examples/enum_keypath_example.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use key_paths_core::EnumKeyPath;
12
use key_paths_core::enum_keypath;
23

34
#[derive(Debug)]
@@ -18,18 +19,16 @@ enum SomeOtherStatus {
1819
Inactive,
1920
}
2021

21-
2222
fn main() {
2323
// ---------- EnumPath ----------
2424
let cp = enum_keypath!(Status::Active(User));
2525
let cp2 = enum_keypath!(Status::Inactive(()));
2626

27-
2827
let cp3 = enum_keypath!(SomeOtherStatus::Active(String));
2928
if let Some(x) = cp3.extract(&SomeOtherStatus::Active("Hello".to_string())) {
3029
println!("Active: {:?}", x);
3130
}
32-
31+
3332
let cp4 = enum_keypath!(SomeOtherStatus::Inactive);
3433
if let Some(x) = cp4.extract(&SomeOtherStatus::Inactive) {
3534
println!("Inactive: {:?}", x);

0 commit comments

Comments
 (0)