1use crate::edi_parse_error::EdiParseError;
2
3use crate::transaction::Transaction;
4
5use crate::tokenizer::SegmentTokens;
6use serde::{Deserialize, Serialize};
7use std::borrow::Cow;
8use std::collections::VecDeque;
9
10#[derive(PartialEq, Debug, Serialize, Deserialize)]
13pub struct FunctionalGroup<'a> {
14 #[serde(borrow)]
18 pub functional_identifier_code: Cow<'a, str>,
19 #[serde(borrow)]
21 pub application_sender_code: Cow<'a, str>,
22 #[serde(borrow)]
24 pub application_receiver_code: Cow<'a, str>,
25 #[serde(borrow)]
27 pub date: Cow<'a, str>,
28 #[serde(borrow)]
34 pub time: Cow<'a, str>,
35 #[serde(borrow)]
38 pub group_control_number: Cow<'a, str>,
39 #[serde(borrow)]
41 pub responsible_agency_code: Cow<'a, str>,
42 #[serde(borrow)]
49 pub version: Cow<'a, str>,
50 #[serde(borrow = "'a")]
52 pub transactions: VecDeque<Transaction<'a>>,
53}
54
55impl<'a> FunctionalGroup<'a> {
56 pub(crate) fn parse_from_tokens(
58 input: SegmentTokens<'a>,
59 ) -> Result<FunctionalGroup<'a>, EdiParseError> {
60 let elements: Vec<&str> = input.iter().map(|x| x.trim()).collect();
61 edi_assert!(
64 elements[0] == "GS",
65 "attempted to parse GS from non-GS segment",
66 input
67 );
68 edi_assert!(
69 elements.len() >= 9,
70 "GS segment does not contain enough elements. At least 9 required",
71 input
72 );
73 let (
74 functional_identifier_code,
75 application_sender_code,
76 application_receiver_code,
77 date,
78 time,
79 group_control_number,
80 responsible_agency_code,
81 version,
82 ) = (
83 Cow::from(elements[1]),
84 Cow::from(elements[2]),
85 Cow::from(elements[3]),
86 Cow::from(elements[4]),
87 Cow::from(elements[5]),
88 Cow::from(elements[6]),
89 Cow::from(elements[7]),
90 Cow::from(elements[8]),
91 );
92
93 Ok(FunctionalGroup {
94 functional_identifier_code,
95 application_sender_code,
96 application_receiver_code,
97 date,
98 time,
99 group_control_number,
100 responsible_agency_code,
101 version,
102 transactions: VecDeque::new(),
103 })
104 }
105
106 pub(crate) fn add_transaction(
108 &mut self,
109 tokens: SegmentTokens<'a>,
110 ) -> Result<(), EdiParseError> {
111 self.transactions
112 .push_back(Transaction::parse_from_tokens(tokens)?);
113 Ok(())
114 }
115
116 pub(crate) fn add_generic_segment(
118 &mut self,
119 tokens: SegmentTokens<'a>,
120 ) -> Result<(), EdiParseError> {
121 if let Some(transaction) = self.transactions.back_mut() {
122 transaction.add_generic_segment(tokens)
123 } else {
124 Err(EdiParseError::new(
125 "unable to enqueue generic segment when no transactions have been enqueued",
126 Some(tokens),
127 ))
128 }
129 }
130
131 pub(crate) fn validate_functional_group(
133 &self,
134 tokens: SegmentTokens<'a>,
135 ) -> Result<(), EdiParseError> {
136 edi_assert!(
137 tokens[0] == "GE",
138 "attempted to call GE verification on non-GE segment",
139 tokens
140 );
141 edi_assert!(
142 self.transactions.len() == str::parse::<usize>(tokens[1]).unwrap(),
143 "functional group validation failed: incorrect number of transactions",
144 self.transactions.len(),
145 str::parse::<usize>(tokens[1]).unwrap(),
146 tokens
147 );
148 edi_assert!(
149 self.group_control_number == tokens[2],
150 "functional group validation failed: mismatched ID",
151 self.group_control_number,
152 tokens[2],
153 tokens
154 );
155 Ok(())
156 }
157
158 pub(crate) fn validate_transaction(
160 &self,
161 tokens: SegmentTokens<'a>,
162 ) -> Result<(), EdiParseError> {
163 if let Some(transaction) = self.transactions.back() {
164 transaction.validate_transaction(tokens)
165 } else {
166 Err(EdiParseError::new(
167 "unable to validate nonexistent transaction",
168 Some(tokens),
169 ))
170 }
171 }
172
173 pub fn to_x12_string(&self, segment_delimiter: char, element_delimiter: char) -> String {
175 let header = String::from("GS");
176 let elements_of_gs = [
177 self.functional_identifier_code.clone(),
178 self.application_sender_code.clone(),
179 self.application_receiver_code.clone(),
180 self.date.clone(),
181 self.time.clone(),
182 self.group_control_number.clone(),
183 self.responsible_agency_code.clone(),
184 self.version.clone(),
185 ];
186
187 let mut buffer = elements_of_gs.iter().fold(header, |mut acc, elem| {
188 acc.push(element_delimiter);
189 acc.push_str(elem);
190 acc
191 });
192 let transactions = self
193 .transactions
194 .iter()
195 .fold(String::new(), |mut acc, transaction| {
196 acc.push(segment_delimiter);
197 acc.push_str(&transaction.to_x12_string(segment_delimiter, element_delimiter));
198 acc
199 });
200
201 buffer.push_str(&transactions);
202
203 let mut closer = String::from("GE");
204 closer.push(element_delimiter);
205 closer.push_str(&self.transactions.len().to_string());
206 closer.push(element_delimiter);
207 closer.push_str(&self.group_control_number);
208
209 buffer.push(segment_delimiter);
210 buffer.push_str(&closer);
211 buffer
212 }
213}
214
215#[test]
216fn functional_group_to_string() {
217 use crate::GenericSegment;
218 use std::iter::FromIterator;
219 let segments = VecDeque::from_iter(vec![
220 GenericSegment {
221 segment_abbreviation: Cow::from("BGN"),
222 elements: ["20", "TEST_ID", "200615", "0000"]
223 .iter()
224 .map(|x| Cow::from(*x))
225 .collect::<VecDeque<Cow<str>>>(),
226 },
227 GenericSegment {
228 segment_abbreviation: Cow::from("BGN"),
229 elements: ["15", "OTHER_TEST_ID", "", "", "END"]
230 .iter()
231 .map(|x| Cow::from(*x))
232 .collect::<VecDeque<Cow<str>>>(),
233 },
234 ]);
235 let transaction = Transaction {
236 transaction_code: Cow::from("140"),
237 transaction_name: Cow::from(""),
238 transaction_set_control_number: Cow::from("100000001"),
239 implementation_convention_reference: None,
240 segments,
241 };
242
243 let functional_group = FunctionalGroup {
244 functional_identifier_code: Cow::from("PO"),
245 application_sender_code: Cow::from("SENDERGS"),
246 application_receiver_code: Cow::from("007326879"),
247 date: Cow::from("20020226"),
248 time: Cow::from("1534"),
249 group_control_number: Cow::from("1"),
250 responsible_agency_code: Cow::from("X"),
251 version: Cow::from("004010"),
252 transactions: VecDeque::from_iter(vec![transaction]),
253 };
254 assert_eq!(functional_group.to_x12_string('\n', '*'), "GS*PO*SENDERGS*007326879*20020226*1534*1*X*004010\nST*140*100000001*\nBGN*20*TEST_ID*200615*0000\nBGN*15*OTHER_TEST_ID***END\nSE*4*100000001\nGE*1*1");
255}
256
257#[test]
258fn construct_functional_group() {
259 let expected_result = FunctionalGroup {
260 functional_identifier_code: Cow::from("PO"),
261 application_sender_code: Cow::from("SENDERGS"),
262 application_receiver_code: Cow::from("007326879"),
263 date: Cow::from("20020226"),
264 time: Cow::from("1534"),
265 group_control_number: Cow::from("1"),
266 responsible_agency_code: Cow::from("X"),
267 version: Cow::from("004010"),
268 transactions: VecDeque::new(),
269 };
270
271 let test_input = vec![
272 "GS",
273 "PO",
274 "SENDERGS",
275 "007326879",
276 "20020226",
277 "1534",
278 "1",
279 "X",
280 "004010",
281 ];
282
283 assert_eq!(
284 FunctionalGroup::parse_from_tokens(test_input).unwrap(),
285 expected_result
286 );
287}