serde_java_properties/lib.rs
1#![warn(missing_docs)]
2
3//! # Serde Java Properties
4//!
5//! [Java Properties](https://en.wikipedia.org/wiki/.properties) is a simple, line-oriented
6//! format for specifying key-value resources used in Java programs. This crate offers
7//! basic (de-)serializers for use with [serde](https://serde.rs)-enabled datastructures.
8//!
9//! ```properties
10//! field_a: a value
11//! field_b: 100
12//! field_c: true
13//! ```
14//!
15//! ## Implementation
16//!
17//! Internally, the [`java-properties` crate](https://crates.io/crates/java-properties) is used
18//! for iterating key-value pairs in an input stream, and writing key-value pairs to an output
19//! stream.
20//!
21//! ## Deserializing a struct
22//!
23//! Usually, the format is untyped i.e. it deserialises to a map from [`String`] to [`String`]. This
24//! crate uses the default [`std::str::FromStr`] implementations for integers, floats and [`bool`] to
25//! provide a typed interface on top of that. That way, simple structures or maps that implement
26//! [`serde::Deserialize`] can be loaded from properties files.
27//!
28//! ```
29//! # use serde::Deserialize;
30//! #
31//! #[derive(Debug, PartialEq, Deserialize)]
32//! struct Data {
33//! field_a: String,
34//! field_b: usize,
35//! field_c: bool,
36//! }
37//! let text = "
38//! field_a: a value
39//! field_b: 100
40//! field_c: true
41//! ";
42//!
43//! let data: Data = serde_java_properties::from_str(text).unwrap();
44//!
45//! assert_eq!(data.field_a, "a value");
46//! assert_eq!(data.field_b, 100);
47//! assert_eq!(data.field_c, true);
48//! ```
49//!
50//! ## Serializing a struct
51//!
52//! Serialization uses the default [`std::fmt::Display`] implementations for each primitive type.
53//!
54//! Supported in the top-level [`Serializer`]:
55//! - Maps
56//! - Structs
57//! - Enums of struct variants
58//! - Options of all of these
59//!
60//! Supported in the field-level Serializer:
61//! - Integers (`i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`)
62//! - Floats (`f32`, `f64`)
63//! - Booleans (`true` or `false`)
64//! - Strings
65//! - Enums of unit variants
66//! - Options of all of these
67//!
68//! ```
69//! # use serde::Serialize;
70//! #
71//! #[derive(Debug, PartialEq, Serialize)]
72//! struct Data {
73//! field_a: String,
74//! field_b: usize,
75//! field_c: bool,
76//! }
77//!
78//! let data = Data { field_a: "value".to_string(), field_b: 100, field_c: true };
79//! let string = serde_java_properties::to_string(&data).unwrap();
80//!
81//! assert_eq!(string, "field_a=value\nfield_b=100\nfield_c=true\n");
82//! ```
83//!
84//! ## Tagged Enums
85//!
86//! Internally tagged enums are generally supported.
87//!
88//! Because of a limitation in serde, type hints are not available in this case, which
89//! means that the [`serde::Deserializer::deserialize_any`] method on the `FieldDeserializer`
90//! is called which only implements a limited heuristic as to which [`serde::de::Visitor`]
91//! method to call.
92//!
93//! ```
94//! use serde::{Deserialize, Serialize};
95//!
96//! #[derive(Debug, PartialEq, Deserialize, Serialize)]
97//! #[serde(tag = "type")]
98//! pub enum Test {
99//! Var1 { key: usize, },
100//! Var2 { msg: String, },
101//! }
102//!
103//! let test1 = Test::Var1 { key: 1000 };
104//! let test2 = Test::Var2 { msg: "serde".to_string() };
105//!
106//! let text1 = serde_java_properties::to_string(&test1).unwrap();
107//! let text2 = serde_java_properties::to_string(&test2).unwrap();
108//!
109//! assert_eq!(text1, "type=Var1\nkey=1000\n");
110//! assert_eq!(text2, "type=Var2\nmsg=serde\n");
111//!
112//! let re1: Test = serde_java_properties::from_str(&text1).unwrap();
113//! let re2: Test = serde_java_properties::from_str(&text2).unwrap();
114//! ```
115//!
116//! ## Unit Struct Variants
117//!
118//! For simple enums, the name of the variant is used as the value
119//!
120//! ```
121//! # use serde::{Serialize, Deserialize};
122//! #
123//! #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
124//! enum Switch { On, Off }
125//! #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
126//! struct House { light: Switch, }
127//!
128//! let text = "light: On";
129//! let data: House = serde_java_properties::from_str(&text).unwrap();
130//!
131//! assert_eq!(data.light, Switch::On);
132//! let out = serde_java_properties::to_string(&data).unwrap();
133//!
134//! assert_eq!(out, "light=On\n");
135//! ```
136//!
137//! ## Alternatives
138//!
139//! Similar to the [`java-properties` crate](https://crates.io/crates/java-properties) itself,
140//! this crate is supposed to be an exact match to the format
141//! [as specified in Java](https://docs.oracle.com/javase/10/docs/api/java/util/Properties.html#load(java.io.Reader)).
142//!
143//! If you need a more powerful configuration syntax, that supports nested structs, you
144//! should probably use [HOCON](https://crates.io/crates/hocon).
145
146pub mod de;
147pub mod ser;
148
149use std::io::{self, Read};
150
151pub use de::Deserializer;
152use encoding_rs::UTF_8;
153pub use ser::Serializer;
154
155use de::Error;
156use encoding_rs::Encoding;
157use serde::{de::DeserializeOwned, Deserialize, Serialize};
158
159/// Turn a string into a value of `T`
160///
161/// This should technically be `T: DeserializeOwned`, but the implementation may change in the future
162pub fn from_str<'a, T: Deserialize<'a>>(input: &'a str) -> Result<T, Error> {
163 T::deserialize(de::Deserializer::from_str(input))
164}
165
166/// Turn a byte slice into a value of `T`
167///
168/// This should technically be `T: DeserializeOwned`, but the implementation may change in the future
169///
170/// **Important**: Do not pass a [`str::as_bytes`] to this function. The reader
171/// expects *ISO-8859-1* by default. Use [`from_str`] instead, which sets the correct encoding.
172pub fn from_slice<'a, T: Deserialize<'a>>(input: &'a [u8]) -> Result<T, Error> {
173 T::deserialize(de::Deserializer::from_slice(input))
174}
175
176/// Turn a byte slice into a value of `T` using the given encoding
177///
178/// This should technically be `T: DeserializeOwned`, but the implementation may change in the future
179pub fn from_slice_with_encoding<'a, T: Deserialize<'a>>(
180 input: &'a [u8],
181 encoding: &'static Encoding,
182) -> Result<T, Error> {
183 T::deserialize(de::Deserializer::from_slice_with_encoding(input, encoding))
184}
185
186/// Turn a reader into a value of `T`
187///
188/// **Important**: Do not use this with a [`std::io::Cursor<&str>`]. The reader expects
189/// *ISO-8859-1* by default. Use [`from_str`] instead, which sets the correct encoding.
190pub fn from_reader<T: DeserializeOwned, R: Read>(reader: R) -> Result<T, Error> {
191 T::deserialize(de::Deserializer::from_reader(reader))
192}
193
194/// Turn a reader into a value of `T` using the given encoding
195pub fn from_reader_with_encoding<T: DeserializeOwned, R: Read>(
196 reader: R,
197 encoding: &'static Encoding,
198) -> Result<T, Error> {
199 T::deserialize(de::Deserializer::from_reader_with_encoding(
200 reader, encoding,
201 ))
202}
203
204/// Write a properties file to a string
205///
206/// *Important*: This uses UTF-8 encoding as the result is a Rust [String]
207pub fn to_string<T: Serialize>(value: &T) -> Result<String, ser::Error> {
208 let mut buffer = String::new();
209 let writer = unsafe { buffer.as_mut_vec() };
210 to_writer_with_encoding(value, writer, UTF_8)?;
211 Ok(buffer)
212}
213
214/// Write a properties file to a byte buffer with the specified encoding
215pub fn to_vec_with_encoding<T: Serialize>(
216 value: &T,
217 encoding: &'static Encoding,
218) -> Result<Vec<u8>, ser::Error> {
219 let mut buffer = Vec::new();
220 to_writer_with_encoding(value, &mut buffer, encoding)?;
221 Ok(buffer)
222}
223
224/// Write a properties file to a byte buffer
225///
226/// **Important**: This uses the default encoding *ISO-8859-1*
227pub fn to_vec<T: Serialize>(value: &T) -> Result<Vec<u8>, ser::Error> {
228 let mut buffer = Vec::new();
229 to_writer(value, &mut buffer)?;
230 Ok(buffer)
231}
232
233/// Write a properties file to a [io::Write] implementation
234///
235/// **Important**: This uses the default encoding *ISO-8859-1*
236pub fn to_writer<T: Serialize, W: io::Write>(value: &T, writer: W) -> Result<(), ser::Error> {
237 let serializer = ser::Serializer::from_writer(writer);
238 value.serialize(serializer)?;
239 Ok(())
240}
241
242/// Write a properties file to a [io::Write] implementation using the provided encoding
243pub fn to_writer_with_encoding<T: Serialize, W: io::Write>(
244 value: &T,
245 writer: W,
246 encoding: &'static Encoding,
247) -> Result<(), ser::Error> {
248 let serializer = ser::Serializer::from_writer_with_encoding(writer, encoding);
249 value.serialize(serializer)?;
250 Ok(())
251}