// Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//! Ini
use std::collections::HashMap; use std::collections::hash_map::{IntoIter, Iter, IterMut, Keys}; use std::collections::hash_map::Entry; use std::fs::{File, OpenOptions}; use std::ops::{Index, IndexMut}; use std::char; use std::io::{self, Read, Write}; use std::fmt::{self, Display}; use std::path::Path; use std::str::Chars; use std::borrow::Borrow; use std::hash::Hash; use std::cmp::Eq; use std::error;
#[derive(Debug, PartialEq, Copy, Clone)] pubenum EscapePolicy { /// escape absolutely nothing (dangerous)
Nothing, /// only escape the most necessary things
Basics, /// escape basics and non-ascii characters
BasicsUnicode, /// Escape reserved symbols.
Reserved, /// Escape reserved symbols and non-ascii characters
ReservedUnicode, /// Escape everything that some INI implementations assume
Everything,
}
/// Given a character this returns true if it should be escaped as /// per this policy or false if not. pubfn should_escape(&self, c: char) -> bool { match c { '\\' | '\x00'...'\x1f' | '\x7f'...'\u{00ff}' => self.escape_basics(), ';' | '#' | '=' | ':' => self.escape_reserved(), '\u{0080}'...'\u{FFFF}' => self.escape_unicode(),
_ => false,
}
}
}
// Escape non-INI characters // // Common escape sequences: https://en.wikipedia.org/wiki/INI_file#Escape_characters // // * `\\` \ (a single backslash, escaping the escape character) // * `\0` Null character // * `\a` Bell/Alert/Audible // * `\b` Backspace, Bell character for some applications // * `\t` Tab character // * `\r` Carriage return // * `\n` Line feed // * `\;` Semicolon // * `\#` Number sign // * `\=` Equals sign // * `\:` Colon // * `\x????` Unicode character with hexadecimal code point corresponding to ???? fn escape_str(s: &str, policy: EscapePolicy) -> String { letmut escaped: String = String::with_capacity(s.len()); for c in s.chars() { // if we know this is not something to escape as per policy, we just // write it and continue. if !policy.should_escape(c) {
escaped.push(c); continue;
}
/// A setter which could be used to set key-value pair in a specified section pubstruct SectionSetter<'a> {
ini: &'a mut Ini,
section_name: Option<String>,
}
/// Ini struct #[derive(Clone)] pubstruct Ini {
sections: HashMap<Option<String>, Properties>,
}
impl Ini { /// Create an instance pubfn new() -> Ini {
Ini {
sections: HashMap::new(),
}
}
/// Set with a specified section, `None` is for the general section pubfn with_section<'b, S>(&'b mutself, section: Option<S>) -> SectionSetter<'b> where
S: Into<String>,
{
SectionSetter::new(self, section.map(|s| s.into()))
}
/// Get the immmutable general section pubfn general_section(&self) -> &Properties { self.section(None::<String>)
.expect("There is no general section in this Ini")
}
/// Get the mutable general section pubfn general_section_mut(&mutself) -> &mut Properties { self.section_mut(None::<String>)
.expect("There is no general section in this Ini")
}
/// Get a immutable section pubfn section<'a, S>(&'a self, name: Option<S>) -> Option<&'a Properties> where
S: Into<String>,
{ self.sections.get(&name.map(|s| s.into()))
}
/// Get a mutable section pubfn section_mut<'a, S>(&'a mutself, name: Option<S>) -> Option<&'a mut Properties> where
S: Into<String>,
{ self.sections.get_mut(&name.map(|s| s.into()))
}
/// Get the entry pubfn entry<'a>(&'a mutself, name: Option<String>) -> Entry<Option<String>, Properties> { self.sections.entry(name.map(|s| s.into()))
}
/// Clear all entries pubfn clear<'a>(&mut self) { self.sections.clear()
}
/// Set key-value to a section pubfn set_to<S>(&mutself, section: Option<S>, key: String, value: String) where
S: Into<String>,
{ self.with_section(section).set(key, value);
}
/// Get the value from a section with key /// /// Example: /// /// ``` /// use ini::Ini; /// let input = "[sec]\nabc = def\n"; /// let ini = Ini::load_from_str(input).unwrap(); /// assert_eq!(ini.get_from(Some("sec"), "abc"), Some("def")); /// ``` pubfn get_from<'a, S>(&'a self, section: Option<S>, key: &str) -> Option<&'a str> where
S: Into<String>,
{ matchself.sections.get(§ion.map(|s| s.into())) {
None => None,
Some(ref prop) => match prop.get(key) {
Some(p) => Some(&p[..]),
None => None,
},
}
}
/// Get the value from a section with key, return the default value if it does not exist /// /// Example: /// /// ``` /// use ini::Ini; /// let input = "[sec]\n"; /// let ini = Ini::load_from_str(input).unwrap(); /// assert_eq!(ini.get_from_or(Some("sec"), "key", "default"), "default"); /// ``` pubfn get_from_or<'a, S>(&'a self, section: Option<S>, key: &str, default: &'a str) -> &'a str where
S: Into<String>,
{ matchself.sections.get(§ion.map(|s| s.into())) {
None => default,
Some(ref prop) => match prop.get(key) {
Some(p) => &p[..],
None => default,
},
}
}
/// Get the mutable from a section with key pubfn get_from_mut<'a, S>(&'a mutself, section: Option<S>, key: &str) -> Option<&'color:blue'>'a str> where
S: Into<String>,
{ matchself.sections.get_mut(§ion.map(|s| s.into())) {
None => None,
Some(prop) => prop.get_mut(key).map(|s| &s[..]),
}
}
/// Delete a section, return the properties if it exists pubfn delete<S>(&mutself, section: Option<S>) -> Option<Properties> where
S: Into<String>,
{ self.sections.remove(§ion.map(|s| s.into()))
}
for (k, v) in props.iter() { let k_str = escape_str(&k[..], policy); let v_str = escape_str(&v[..], policy); try!(write!(writer, "{}={}\n", k_str, v_str));
}
}
}
Ok(())
}
}
impl Ini { /// Load from a string pubfn load_from_str(buf: &str) -> Result<Ini, Error> { letmut parser = Parser::new(buf.chars(), false);
parser.parse()
}
/// Load from a string, but do not interpret '\' as an escape character pubfn load_from_str_noescape(buf: &str) -> Result<Ini, Error> { letmut parser = Parser::new(buf.chars(), true);
parser.parse()
}
/// Consume all the white space until the end of the line or a tab fn parse_whitespace(&mutself) { whilelet Some(c) = self.ch { if !c.is_whitespace() && c != '\n' && c != '\t' && c != '\r' { break;
} self.bump();
}
}
/// Consume all the white space except line break fn parse_whitespace_except_line_break(&mutself) { whilelet Some(c) = self.ch { if (c == '\n' || c == '\r' || !c.is_whitespace()) && c != '\t' { break;
} self.bump();
}
}
/// Parse the whole INI input pubfn parse(&mutself) -> Result<Ini, Error> { letmut result = Ini::new(); letmut curkey: String = "".into(); letmut cursec: Option<String> = None;
#[test] fn load_from_str_with_valid_input() { let input = "[sec1]\nkey1=val1\nkey2=377\n[sec2]foo=bar\n"; let opt = Ini::load_from_str(input);
assert!(opt.is_ok());
let output = opt.unwrap();
assert_eq!(output.sections.len(), 2);
assert!(output.sections.contains_key(&Some("sec1".into())));
let sec1 = &output.sections[&Some("sec1".into())];
assert_eq!(sec1.len(), 2); let key1: String = "key1".into();
assert!(sec1.contains_key(&key1)); let key2: String = "key2".into();
assert!(sec1.contains_key(&key2)); let val1: String = "val1".into();
assert_eq!(sec1[&key1], val1); let val2: String = "377".into();
assert_eq!(sec1[&key2], val2);
}
#[test] fn load_from_str_without_ending_newline() { let input = "[sec1]\nkey1=val1\nkey2=377\n[sec2]foo=bar"; let opt = Ini::load_from_str(input);
assert!(opt.is_ok());
}
#[test] fn test_parse_comment() { let input = "; abcdefghijklmn\n"; let opt = Ini::load_from_str(input);
assert!(opt.is_ok());
}
#[test] fn test_inline_comment() { let input = "
[section name]
name = hello # abcdefg
gender = mail ; abdddd "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(ini.get_from(Some("section name"), "name").unwrap(), "hello");
}
#[test] fn test_sharp_comment() { let input = "
[section name]
name = hello # abcdefg "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(ini.get_from(Some("section name"), "name").unwrap(), "hello");
}
#[test] fn test_iter() { let input = "
[section name]
name = hello # abcdefg
gender = mail ; abdddd "; letmut ini = Ini::load_from_str(input).unwrap();
for (_, _) in &mut ini {} for (_, _) in &ini {} for (_, _) in ini {}
}
#[test] fn test_colon() { let input = "
[section name]
name: hello # abcdefg
gender : mail ; abdddd "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(ini.get_from(Some("section name"), "name").unwrap(), "hello");
assert_eq!(
ini.get_from(Some("section name"), "gender").unwrap(), "mail"
);
}
#[test] fn test_string() { let input = "
[section name] # This is a comment
Key = \"Value\" "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(ini.get_from(Some("section name"), "Key").unwrap(), "Value");
}
#[test] fn test_string_multiline() { let input = "
[section name] # This is a comment
Key = \"Value
Otherline\" "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(
ini.get_from(Some("section name"), "Key").unwrap(), "Value\nOtherline"
);
}
#[test] fn test_string_comment() { let input = "
[section name] # This is a comment
Key = \"Value # This is not a comment ; at all\"
Stuff = Other "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(
ini.get_from(Some("section name"), "Key").unwrap(), "Value # This is not a comment ; at all"
);
}
#[test] fn test_string_single() { let input = "
[section name] # This is a comment
Key = 'Value'
Stuff = Other "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(ini.get_from(Some("section name"), "Key").unwrap(), "Value");
}
#[test] fn test_string_includes_quote() { let input = "
[Test]
Comment[tr]=İnternet'e erişin
Comment[uk]=Доступ до Інтернету "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(
ini.get_from(Some("Test"), "Comment[tr]").unwrap(), "İnternet'e erişin"
);
}
#[test] fn test_string_single_multiline() { let input = "
[section name] # This is a comment
Key = 'Value
Otherline'
Stuff = Other "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(
ini.get_from(Some("section name"), "Key").unwrap(), "Value\nOtherline"
);
}
#[test] fn test_string_single_comment() { let input = "
[section name] # This is a comment
Key = 'Value # This is not a comment ; at all' "; let ini = Ini::load_from_str(input).unwrap();
assert_eq!(
ini.get_from(Some("section name"), "Key").unwrap(), "Value # This is not a comment ; at all"
);
}
#[test] fn load_from_str_with_valid_empty_input() { let input = "key1=\nkey2=val2\n"; let opt = Ini::load_from_str(input);
assert!(opt.is_ok());
let output = opt.unwrap();
assert_eq!(output.sections.len(), 1);
assert!(output.sections.contains_key(&None::<String>));
let sec1 = &output.sections[&None::<String>];
assert_eq!(sec1.len(), 2); let key1: String = "key1".into();
assert!(sec1.contains_key(&key1)); let key2: String = "key2".into();
assert!(sec1.contains_key(&key2)); let val1: String = "".into();
assert_eq!(sec1[&key1], val1); let val2: String = "val2".into();
assert_eq!(sec1[&key2], val2);
}
#[test] fn load_from_str_with_crlf() { let input = "key1=val1\r\nkey2=val2\r\n"; let opt = Ini::load_from_str(input);
assert!(opt.is_ok());
let output = opt.unwrap();
assert_eq!(output.sections.len(), 1);
assert!(output.sections.contains_key(&None::<String>)); let sec1 = &output.sections[&None::<String>];
assert_eq!(sec1.len(), 2); let key1: String = "key1".into();
assert!(sec1.contains_key(&key1)); let key2: String = "key2".into();
assert!(sec1.contains_key(&key2)); let val1: String = "val1".into();
assert_eq!(sec1[&key1], val1); let val2: String = "val2".into();
assert_eq!(sec1[&key2], val2);
}
#[test] fn load_from_str_with_cr() { let input = "key1=val1\rkey2=val2\r"; let opt = Ini::load_from_str(input);
assert!(opt.is_ok());
let output = opt.unwrap();
assert_eq!(output.sections.len(), 1);
assert!(output.sections.contains_key(&None::<String>)); let sec1 = &output.sections[&None::<String>];
assert_eq!(sec1.len(), 2); let key1: String = "key1".into();
assert!(sec1.contains_key(&key1)); let key2: String = "key2".into();
assert!(sec1.contains_key(&key2)); let val1: String = "val1".into();
assert_eq!(sec1[&key1], val1); let val2: String = "val2".into();
assert_eq!(sec1[&key2], val2);
}
#[test] fn get_with_non_static_key() { let input = "key1=val1\nkey2=val2\n"; let opt = Ini::load_from_str(input).unwrap();
let sec1 = &opt.sections[&None::<String>];
let key = "key1".to_owned();
sec1.get(&key).unwrap();
}
#[test] fn load_from_str_noescape() { let input = "path=C:\\Windows\\Some\\Folder\\"; let opt = Ini::load_from_str_noescape(input);
assert!(opt.is_ok());
let output = opt.unwrap();
assert_eq!(output.sections.len(), 1); let sec = &output.sections[&None::<String>];
assert_eq!(sec.len(), 1);
assert!(sec.contains_key("path"));
assert_eq!(sec["path"], "C:\\Windows\\Some\\Folder\\");
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.43 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.