spring_batch_rs/item/fake/
person_reader.rs1use std::cell::Cell;
2use std::fmt;
3
4use ::serde::{ser::Error, Deserialize, Serialize, Serializer};
5use fake::faker::internet::raw::*;
6use fake::locales::*;
7use fake::{faker::name::raw::*, Fake};
8use log::debug;
9use rand::RngExt;
10
11use time::format_description;
12use time::{Date, Month};
13
14use crate::core::item::ItemReader;
15use crate::core::item::ItemReaderResult;
16
17#[derive(Serialize, Deserialize, Clone)]
19pub struct Person {
20 first_name: String,
21 last_name: String,
22 title: String,
23 email: String,
24 #[serde(serialize_with = "date_serializer")]
25 birth_date: Date,
26}
27
28fn date_serializer<S>(date: &Date, serializer: S) -> Result<S::Ok, S::Error>
30where
31 S: Serializer,
32{
33 let result = format_description::parse("[year]-[month]-[day]");
34
35 match result {
36 Ok(format) => {
37 let s = date.format(&format).unwrap();
38 serializer.serialize_str(&s)
39 }
40 Err(error) => Err(Error::custom(error.to_string())),
41 }
42}
43
44impl fmt::Display for Person {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(
47 f,
48 "first_name:{}, last_name:{}, birth_date:{}",
49 self.first_name, self.last_name, self.birth_date
50 )
51 }
52}
53
54pub struct PersonReader {
77 count: Cell<usize>,
78}
79
80impl ItemReader<Person> for PersonReader {
81 fn read(&self) -> ItemReaderResult<Person> {
86 if self.count.get() == 0 {
87 return Ok(None);
88 }
89
90 self.count.set(self.count.get() - 1);
91
92 let person = Person {
93 first_name: FirstName(FR_FR).fake(),
94 last_name: LastName(FR_FR).fake(),
95 title: Title(FR_FR).fake(),
96 email: FreeEmail(FR_FR).fake(),
97 birth_date: fake_date(),
98 };
99 debug!("Person: {}", person);
100 Ok(Some(person))
101 }
102}
103
104fn fake_date() -> Date {
106 let mut rng = rand::rng();
107 let year = rng.random_range(1900..2022);
108 let month = rng.random_range(1..=12);
109 let day = rng.random_range(1..=28);
110
111 Date::from_calendar_date(year, Month::try_from(month).unwrap(), day).unwrap()
112}
113
114#[derive(Default)]
116pub struct PersonReaderBuilder {
117 number_of_items: usize,
118}
119
120impl PersonReaderBuilder {
121 pub fn new() -> Self {
132 Self { number_of_items: 0 }
133 }
134
135 pub fn number_of_items(mut self, number_of_items: usize) -> Self {
146 self.number_of_items = number_of_items;
147 self
148 }
149
150 pub fn build(self) -> PersonReader {
170 PersonReader {
171 count: self.number_of_items.into(),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::{PersonReader, PersonReaderBuilder};
179 use crate::core::item::ItemReader;
180
181 #[test]
182 fn should_read_configured_number_of_persons() {
183 let reader: PersonReader = PersonReaderBuilder::new().number_of_items(2).build();
184 assert_eq!(reader.count.get(), 2);
185
186 let result1 = reader.read();
187 assert_eq!(reader.count.get(), 1);
188 assert!(result1.is_ok());
189
190 let person = result1.unwrap();
191 assert!(person.is_some());
192 assert!(!person.as_ref().unwrap().first_name.is_empty());
193 assert!(!person.as_ref().unwrap().last_name.is_empty());
194
195 let result2 = reader.read();
196 assert_eq!(reader.count.get(), 0);
197 assert!(result2.is_ok());
198 assert!(result2.unwrap().is_some());
199
200 let result3 = reader.read();
201 assert_eq!(reader.count.get(), 0);
202 assert!(result3.unwrap().is_none());
203 }
204
205 #[test]
206 fn should_display_person_with_all_fields() {
207 let reader = PersonReaderBuilder::new().number_of_items(1).build();
208 let person = reader.read().unwrap().unwrap();
209 let text = format!("{}", person);
210 assert!(
211 text.contains("first_name:"),
212 "Display missing first_name: {text}"
213 );
214 assert!(
215 text.contains("last_name:"),
216 "Display missing last_name: {text}"
217 );
218 assert!(
219 text.contains("birth_date:"),
220 "Display missing birth_date: {text}"
221 );
222 }
223
224 #[test]
225 fn should_serialize_person_with_date_in_iso_format() {
226 let reader = PersonReaderBuilder::new().number_of_items(1).build();
227 let person = reader.read().unwrap().unwrap();
228 let json = serde_json::to_string(&person).expect("serialization must succeed");
229 assert!(
230 json.contains("birth_date"),
231 "missing birth_date key: {json}"
232 );
233 let hyphen_count = json.chars().filter(|&c| c == '-').count();
235 assert!(hyphen_count >= 2, "date should contain hyphens: {json}");
236 }
237}