Skip to content

Commit efa53f4

Browse files
authored
Merge pull request #31 from jacobneiltaylor/str-dict-load
Add support for building dictionaries from string slices
2 parents 89c5c0a + bd78d23 commit efa53f4

File tree

1 file changed

+273
-17
lines changed

1 file changed

+273
-17
lines changed

src/protocol/dictionary.rs

Lines changed: 273 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,29 +127,48 @@ pub struct Dictionary {
127127

128128
#[allow(unused)]
129129
impl Dictionary {
130+
fn from_lines(lines: StringIterator) -> Result<Dictionary, RadiusError> {
131+
let mut attributes: Vec<DictionaryAttribute> = Vec::new();
132+
let mut values: Vec<DictionaryValue> = Vec::new();
133+
let mut vendors: Vec<DictionaryVendor> = Vec::new();
134+
135+
match parse_lines(lines, &mut attributes, &mut values, &mut vendors) {
136+
Ok(()) => Ok(Dictionary { attributes, values, vendors }),
137+
Err(error) => Err(error),
138+
}
139+
}
140+
130141
/// Creates Dictionary from a string
131142
pub fn from_str(dictionary_str: &str) -> Result<Dictionary, RadiusError> {
132-
todo!()
143+
let lines = read_str(dictionary_str);
144+
Dictionary::from_lines(lines)
133145
}
134146

135147
/// Creates Dictionary from a RADIUS dictionary file
136148
pub fn from_file(file_path: &str) -> Result<Dictionary, RadiusError> {
137-
let mut attributes: Vec<DictionaryAttribute> = Vec::new();
138-
let mut values: Vec<DictionaryValue> = Vec::new();
139-
let mut vendors: Vec<DictionaryVendor> = Vec::new();
140-
141-
match parse_file(file_path, &mut attributes, &mut values, &mut vendors) {
142-
Ok(()) => Ok(Dictionary { attributes, values, vendors }),
149+
match read_file(file_path) {
150+
Ok(lines) => Dictionary::from_lines(lines),
143151
Err(error) => Err(error)
144152
}
145153
}
146154

155+
/// The add functions process attributes, values and vendors from a supplied dictionary file
156+
/// and merge them into an existing set of attributes, values and vendors
157+
158+
/// Adds a dictionary string to existing Dictionary
159+
pub fn add_str(&mut self, dictionary_str: &str) -> Result<(), RadiusError> {
160+
let lines = read_str(dictionary_str);
161+
parse_lines(lines, &mut self.attributes, &mut self.values, &mut self.vendors)
162+
}
163+
147164
/// Adds a dictionary file to existing Dictionary
148-
///
149-
/// Processes attributes, values and vendors from supplied dictionary file
150-
/// and adds them to existing attributes, values and vendors
151165
pub fn add_file(&mut self, file_path: &str) -> Result<(), RadiusError> {
152-
parse_file(file_path, &mut self.attributes, &mut self.values, &mut self.vendors)
166+
match read_file(file_path) {
167+
Ok(lines) => parse_lines(
168+
lines, &mut self.attributes, &mut self.values, &mut self.vendors
169+
),
170+
Err(error) => Err(error)
171+
}
153172
}
154173

155174
/// Returns parsed DictionaryAttributes
@@ -191,14 +210,29 @@ fn assign_attribute_type(code_type: &str) -> Option<SupportedAttributeTypes> {
191210
}
192211
}
193212

194-
fn parse_file(file_path: &str, attributes: &mut Vec<DictionaryAttribute>, values: &mut Vec<DictionaryValue>, vendors: &mut Vec<DictionaryVendor>) -> Result<(), RadiusError> {
195-
let mut vendor_name: String = String::new();
213+
type StringIterator = Box<dyn Iterator<Item = String>>;
214+
215+
fn filter_lines<T: Iterator<Item = String> + 'static>(lines: T) -> StringIterator {
216+
Box::new(
217+
lines
218+
.filter(|line| !line.is_empty())
219+
.filter(|line| !line.contains(&COMMENT_PREFIX))
220+
)
221+
}
196222

223+
fn read_file(file_path: &str) -> Result<StringIterator, RadiusError> {
197224
let reader = io::BufReader::new(File::open(file_path).map_err(|error| RadiusError::MalformedDictionaryError { error })?);
198-
let lines = reader.lines()
199-
.filter_map(Result::ok)
200-
.filter(|line| !line.is_empty())
201-
.filter(|line| !line.contains(&COMMENT_PREFIX));
225+
Ok(filter_lines(reader.lines().filter_map(Result::ok)))
226+
}
227+
228+
fn read_str(dictionary_str: &str) -> StringIterator {
229+
let lines: Vec<String> = dictionary_str.to_string().lines()
230+
.map(|line| line.to_owned()).collect();
231+
filter_lines(lines.into_iter())
232+
}
233+
234+
fn parse_lines(lines: StringIterator, attributes: &mut Vec<DictionaryAttribute>, values: &mut Vec<DictionaryValue>, vendors: &mut Vec<DictionaryVendor>) -> Result<(), RadiusError>{
235+
let mut vendor_name: String = String::new();
202236

203237
for line in lines {
204238
let parsed_line: Vec<&str> = line.split_whitespace().filter(|&item| !item.is_empty()).collect();
@@ -249,6 +283,116 @@ fn parse_vendor(parsed_line: Vec<&str>, vendors: &mut Vec<DictionaryVendor>) {
249283
mod tests {
250284
use super::*;
251285

286+
#[test]
287+
fn test_from_str() {
288+
let dictionary_str = include_str!("../../dict_examples/test_dictionary_dict");
289+
290+
let dict = Dictionary::from_str(dictionary_str).unwrap();
291+
292+
let mut attributes: Vec<DictionaryAttribute> = Vec::new();
293+
attributes.push(DictionaryAttribute {
294+
name: "User-Name".to_string(),
295+
vendor_name: "".to_string(),
296+
code: 1,
297+
code_type: Some(SupportedAttributeTypes::AsciiString)
298+
});
299+
attributes.push(DictionaryAttribute {
300+
name: "NAS-IP-Address".to_string(),
301+
vendor_name: "".to_string(),
302+
code: 4,
303+
code_type: Some(SupportedAttributeTypes::IPv4Addr)
304+
});
305+
attributes.push(DictionaryAttribute {
306+
name: "NAS-Port-Id".to_string(),
307+
vendor_name: "".to_string(),
308+
code: 5,
309+
code_type: Some(SupportedAttributeTypes::Integer)
310+
});
311+
attributes.push(DictionaryAttribute {
312+
name: "Framed-Protocol".to_string(),
313+
vendor_name: "".to_string(),
314+
code: 7,
315+
code_type: Some(SupportedAttributeTypes::Integer)
316+
});
317+
attributes.push(DictionaryAttribute {
318+
name: "Chargeable-User-Identity".to_string(),
319+
vendor_name: "".to_string(),
320+
code: 89,
321+
code_type: Some(SupportedAttributeTypes::ByteString)
322+
});
323+
attributes.push(DictionaryAttribute {
324+
name: "Delegated-IPv6-Prefix".to_string(),
325+
vendor_name: "".to_string(),
326+
code: 123,
327+
code_type: Some(SupportedAttributeTypes::IPv6Prefix)
328+
});
329+
attributes.push(DictionaryAttribute {
330+
name: "MIP6-Feature-Vector".to_string(),
331+
vendor_name: "".to_string(),
332+
code: 124,
333+
code_type: Some(SupportedAttributeTypes::Integer64)
334+
});
335+
attributes.push(DictionaryAttribute {
336+
name: "Mobile-Node-Identifier".to_string(),
337+
vendor_name: "".to_string(),
338+
code: 145,
339+
code_type: Some(SupportedAttributeTypes::ByteString)
340+
});
341+
attributes.push(DictionaryAttribute {
342+
name: "PMIP6-Home-Interface-ID".to_string(),
343+
vendor_name: "".to_string(),
344+
code: 153,
345+
code_type: Some(SupportedAttributeTypes::InterfaceId)
346+
});
347+
attributes.push(DictionaryAttribute {
348+
name: "PMIP6-Home-IPv4-HoA".to_string(),
349+
vendor_name: "".to_string(),
350+
code: 155,
351+
code_type: Some(SupportedAttributeTypes::IPv4Prefix)
352+
});
353+
attributes.push(DictionaryAttribute {
354+
name: "Somevendor-Name".to_string(),
355+
vendor_name: "Somevendor".to_string(),
356+
code: 1,
357+
code_type: Some(SupportedAttributeTypes::AsciiString)
358+
});
359+
attributes.push(DictionaryAttribute {
360+
name: "Somevendor-Number".to_string(),
361+
vendor_name: "Somevendor".to_string(),
362+
code: 2,
363+
code_type: Some(SupportedAttributeTypes::Integer)
364+
});
365+
attributes.push(DictionaryAttribute {
366+
name: "Class".to_string(),
367+
vendor_name: "".to_string(),
368+
code: 25,
369+
code_type: Some(SupportedAttributeTypes::ByteString)
370+
});
371+
372+
let mut values: Vec<DictionaryValue> = Vec::new();
373+
values.push(DictionaryValue {
374+
attribute_name: "Framed-Protocol".to_string(),
375+
value_name: "PPP".to_string(),
376+
vendor_name: "".to_string(),
377+
value: "1".to_string()
378+
});
379+
values.push(DictionaryValue {
380+
attribute_name: "Somevendor-Number".to_string(),
381+
value_name: "Two".to_string(),
382+
vendor_name: "Somevendor".to_string(),
383+
value: "2".to_string()
384+
});
385+
386+
let mut vendors: Vec<DictionaryVendor> = Vec::new();
387+
vendors.push(DictionaryVendor {
388+
name: "Somevendor".to_string(),
389+
id: 10,
390+
});
391+
392+
let expected_dict = Dictionary { attributes, values, vendors };
393+
assert_eq!(dict, expected_dict)
394+
}
395+
252396
#[test]
253397
fn test_from_file() {
254398
let dictionary_path = "./dict_examples/test_dictionary_dict";
@@ -359,6 +503,118 @@ mod tests {
359503
assert_eq!(dict, expected_dict)
360504
}
361505

506+
#[test]
507+
fn test_add_str() {
508+
let empty_dictionary_str = include_str!("../../dict_examples/empty_test_dictionary_dict");
509+
let dictionary_str = include_str!("../../dict_examples/test_dictionary_dict");
510+
511+
let mut dict = Dictionary::from_str(empty_dictionary_str).unwrap();
512+
dict.add_str(dictionary_str).unwrap();
513+
514+
let mut attributes: Vec<DictionaryAttribute> = Vec::new();
515+
attributes.push(DictionaryAttribute {
516+
name: "User-Name".to_string(),
517+
vendor_name: "".to_string(),
518+
code: 1,
519+
code_type: Some(SupportedAttributeTypes::AsciiString)
520+
});
521+
attributes.push(DictionaryAttribute {
522+
name: "NAS-IP-Address".to_string(),
523+
vendor_name: "".to_string(),
524+
code: 4,
525+
code_type: Some(SupportedAttributeTypes::IPv4Addr)
526+
});
527+
attributes.push(DictionaryAttribute {
528+
name: "NAS-Port-Id".to_string(),
529+
vendor_name: "".to_string(),
530+
code: 5,
531+
code_type: Some(SupportedAttributeTypes::Integer)
532+
});
533+
attributes.push(DictionaryAttribute {
534+
name: "Framed-Protocol".to_string(),
535+
vendor_name: "".to_string(),
536+
code: 7,
537+
code_type: Some(SupportedAttributeTypes::Integer)
538+
});
539+
attributes.push(DictionaryAttribute {
540+
name: "Chargeable-User-Identity".to_string(),
541+
vendor_name: "".to_string(),
542+
code: 89,
543+
code_type: Some(SupportedAttributeTypes::ByteString)
544+
});
545+
attributes.push(DictionaryAttribute {
546+
name: "Delegated-IPv6-Prefix".to_string(),
547+
vendor_name: "".to_string(),
548+
code: 123,
549+
code_type: Some(SupportedAttributeTypes::IPv6Prefix)
550+
});
551+
attributes.push(DictionaryAttribute {
552+
name: "MIP6-Feature-Vector".to_string(),
553+
vendor_name: "".to_string(),
554+
code: 124,
555+
code_type: Some(SupportedAttributeTypes::Integer64)
556+
});
557+
attributes.push(DictionaryAttribute {
558+
name: "Mobile-Node-Identifier".to_string(),
559+
vendor_name: "".to_string(),
560+
code: 145,
561+
code_type: Some(SupportedAttributeTypes::ByteString)
562+
});
563+
attributes.push(DictionaryAttribute {
564+
name: "PMIP6-Home-Interface-ID".to_string(),
565+
vendor_name: "".to_string(),
566+
code: 153,
567+
code_type: Some(SupportedAttributeTypes::InterfaceId)
568+
});
569+
attributes.push(DictionaryAttribute {
570+
name: "PMIP6-Home-IPv4-HoA".to_string(),
571+
vendor_name: "".to_string(),
572+
code: 155,
573+
code_type: Some(SupportedAttributeTypes::IPv4Prefix)
574+
});
575+
attributes.push(DictionaryAttribute {
576+
name: "Somevendor-Name".to_string(),
577+
vendor_name: "Somevendor".to_string(),
578+
code: 1,
579+
code_type: Some(SupportedAttributeTypes::AsciiString)
580+
});
581+
attributes.push(DictionaryAttribute {
582+
name: "Somevendor-Number".to_string(),
583+
vendor_name: "Somevendor".to_string(),
584+
code: 2,
585+
code_type: Some(SupportedAttributeTypes::Integer)
586+
});
587+
attributes.push(DictionaryAttribute {
588+
name: "Class".to_string(),
589+
vendor_name: "".to_string(),
590+
code: 25,
591+
code_type: Some(SupportedAttributeTypes::ByteString)
592+
});
593+
594+
let mut values: Vec<DictionaryValue> = Vec::new();
595+
values.push(DictionaryValue {
596+
attribute_name: "Framed-Protocol".to_string(),
597+
value_name: "PPP".to_string(),
598+
vendor_name: "".to_string(),
599+
value: "1".to_string()
600+
});
601+
values.push(DictionaryValue {
602+
attribute_name: "Somevendor-Number".to_string(),
603+
value_name: "Two".to_string(),
604+
vendor_name: "Somevendor".to_string(),
605+
value: "2".to_string()
606+
});
607+
608+
let mut vendors: Vec<DictionaryVendor> = Vec::new();
609+
vendors.push(DictionaryVendor {
610+
name: "Somevendor".to_string(),
611+
id: 10,
612+
});
613+
614+
let expected_dict = Dictionary { attributes, values, vendors };
615+
assert_eq!(dict, expected_dict)
616+
}
617+
362618
#[test]
363619
fn test_add_file() {
364620
let empty_dictionary_path = "./dict_examples/empty_test_dictionary_dict";

0 commit comments

Comments
 (0)