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
129
130
131
132
133
//! A crate to parse the Java Properties file format.
//!
//! The Properties file format is simple key/value text format that can be read
//! and written by the standard library of Java. It is often used for
//! configuration files, and also for translation files (Bundles).
//!
//! The Java class that supports this format is `java.util.Properties`, with
//! its methods `load` and `store`. Note that the Java class also provides the
//! methods `loadFromXML` and `storeToXML`, but this crate does not support
//! the XML format.
//!
//! Starting from Java 9, the Properties files used as Resource Bundles are
//! allowed to be utf-8, instead of only latin1 with unicode escapes ([JEP
//! 226](http://openjdk.java.net/jeps/226)). This crate does not support JEP
//! 226.
//!
//! This crate was written based on the [Java 8 API
//! reference](https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#load-java.io.Reader-).
//! As far as the author knows, this crate supports reading the full Properties
//! format with no exception. There is no support to write Properties files,
//! yet.

#[macro_use]
extern crate nom;

#[macro_use]
#[cfg(test)]
mod test_helpers;

mod parsers;

use std::path::Path;
use std::fs::File;
use std::io;
use std::io::Read;

/// A parsing error
#[derive(Debug)]
pub struct Error;

impl From<io::Error> for Error {
    fn from(_: io::Error) -> Error {
        Error
    }
}

/// Parse the bytes and return a vec of key/value pairs. The order of the
/// elements in the parsed content is preserved.
///
/// Keys are not guaranteed to be unique.
///
/// # Examples
///
/// ```
/// let parsed = dotproperties::parse_from_slice(b"knowledge = power\n1+1 = 2").unwrap();
/// assert_eq!(parsed, vec![("knowledge".to_string(), "power".to_string()),
///                         ("1+1".to_string(), "2".to_string())]);
/// ```
///
/// It is often more convenient to work with a map, instead of a vec of pairs.
/// The conversion can be done using the tools from std.
///
/// ```
/// use std::collections::HashMap;
///
/// let parsed = dotproperties::parse_from_slice(b"a:b\nc:d").unwrap();
/// let mapped : HashMap<_,_> = parsed.into_iter().collect();
///
/// ```
///
/// Note that if you use `collect` to create a map from a vec containing
/// duplicate keys, only the value of the last one is retained.
///
/// ```
/// use std::collections::HashMap;
///
/// let parsed = dotproperties::parse_from_slice(b"a:x\na:y").unwrap();
/// let mapped : HashMap<_,_> = parsed.into_iter().collect();
/// assert_eq!(mapped.len(), 1);
/// // The `map` converts the `Option<&String>` to an `Option<&str>`
/// assert_eq!(mapped.get("a").map(|v| &**v), Some("y"));
///
/// ```
///
/// # Failures
///
/// There is no fine-grained error reporting, yet. If the function encounters
/// a parse error, then an empty struct `Error` is returned.
///
pub fn parse_from_slice(input: &[u8]) -> Result<Vec<(String, String)>, Error> {
    parsers::full_parser(input).to_result().map_err(|_| Error)
}

/// Parse the file and return a vec of key/value pairs. The order of the
/// elements in the parsed content is preserved.
///
/// Keys are not guaranteed to be unique.
///
/// # Examples
///
/// ```
/// # std::fs::File::create("config.properties").unwrap();
/// let parsed = dotproperties::parse_from_file("config.properties").unwrap();
/// # std::fs::remove_file("config.properties").unwrap();
/// ```
///
/// It is often more convenient to work with a map, instead of a vec of pairs.
/// The conversion can be done using the tools from std.
///
/// ```
/// use std::collections::HashMap;
///
/// # std::fs::File::create("config.properties").unwrap();
/// let parsed = dotproperties::parse_from_file("config.properties").unwrap();
/// let mapped : HashMap<_,_> = parsed.into_iter().collect();
/// # std::fs::remove_file("config.properties").unwrap();
///
/// ```
///
/// Note that if you use `collect` to create a map from a vec containing
/// duplicate keys, only the value of the last one is retained.
///
/// # Failures
///
/// There is no fine-grained error reporting, yet. If the function encounters
/// a parse error or an IO error, then an empty struct `Error` is returned.
///
pub fn parse_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<(String, String)>, Error> {
    let mut file = File::open(path)?;
    let mut contents = vec![];
    file.read_to_end(&mut contents)?;
    parse_from_slice(&*contents)
}