-
-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathconfig_file_writer.rs
More file actions
128 lines (111 loc) · 3.81 KB
/
Copy pathconfig_file_writer.rs
File metadata and controls
128 lines (111 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Writers for Hadoop XML config files and Java `.properties` files.
use std::{fmt::Write as _, io::Write};
use java_properties::{PropertiesError, PropertiesWriter};
use snafu::{ResultExt, Snafu};
use xml::escape::escape_str_attribute;
#[derive(Debug, Snafu)]
pub enum PropertiesWriterError {
#[snafu(display("failed to create properties file"))]
Properties { source: PropertiesError },
#[snafu(display("failed to convert properties file byte array to UTF-8"))]
FromUtf8 { source: std::string::FromUtf8Error },
}
/// Creates a common Java properties file string in the format:
/// `property_1=value_1\nproperty_2=value_2\n`.
pub fn to_java_properties_string<'a, T>(properties: T) -> Result<String, PropertiesWriterError>
where
T: Iterator<Item = (&'a String, &'a String)>,
{
let mut output = Vec::new();
write_java_properties(&mut output, properties)?;
String::from_utf8(output).context(FromUtf8Snafu)
}
/// Writes Java properties to the given writer.
fn write_java_properties<'a, W, T>(writer: W, properties: T) -> Result<(), PropertiesWriterError>
where
W: Write,
T: Iterator<Item = (&'a String, &'a String)>,
{
let mut writer = PropertiesWriter::new(writer);
for (k, v) in properties {
writer.write(k, v).context(PropertiesSnafu)?;
}
writer.flush().context(PropertiesSnafu)?;
Ok(())
}
/// Converts properties into a Hadoop configuration XML, including the wrapping
/// `<configuration>...</configuration>` elements.
pub fn to_hadoop_xml<'a, T>(properties: T) -> String
where
T: Iterator<Item = (&'a String, &'a String)>,
{
let mut snippet = String::new();
for (k, v) in properties {
let escaped_key = escape_str_attribute(k);
let escaped_value = escape_str_attribute(v);
write!(
snippet,
" <property>\n <name>{escaped_key}</name>\n <value>{escaped_value}</value>\n </property>\n"
)
.expect("writing to a String is infallible");
}
format!("<?xml version=\"1.0\"?>\n<configuration>\n{snippet}</configuration>")
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
fn xml(pairs: &[(&str, &str)]) -> String {
let map: BTreeMap<String, String> = pairs
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
to_hadoop_xml(map.iter())
}
fn props(pairs: &[(&str, &str)]) -> String {
let map: BTreeMap<String, String> = pairs
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
to_java_properties_string(map.iter()).unwrap()
}
#[test]
fn hadoop_xml_wraps_empty_configuration() {
assert_eq!(
xml(&[]),
"<?xml version=\"1.0\"?>\n<configuration>\n</configuration>"
);
}
#[test]
fn hadoop_xml_renders_single_property() {
assert_eq!(
xml(&[("fs.defaultFS", "hdfs://hdfs/")]),
"<?xml version=\"1.0\"?>\n<configuration>\n \
<property>\n <name>fs.defaultFS</name>\n \
<value>hdfs://hdfs/</value>\n </property>\n</configuration>"
);
}
#[test]
fn hadoop_xml_escapes_special_characters() {
let rendered = xml(&[("k", "<a>&b")]);
assert!(
rendered.contains("<value><a>&b</value>"),
"{rendered}"
);
}
#[test]
fn java_properties_renders_key_value() {
assert_eq!(props(&[("a", "1"), ("b", "2")]), "a=1\nb=2\n");
}
#[test]
fn java_properties_renders_empty() {
assert_eq!(props(&[("empty", "")]), "empty=\n");
}
#[test]
fn java_properties_escapes_colon_in_value() {
assert_eq!(
props(&[("url", "file://this/location/file.abc")]),
"url=file\\://this/location/file.abc\n"
);
}
}