config: Replace rust-ini

This cuts binary size by around 30KiB on my local release builds.
This commit is contained in:
Kenny Levinsen
2020-05-06 23:12:46 +02:00
parent c7068fe8fd
commit 04b9ab2367
5 changed files with 201 additions and 38 deletions

8
Cargo.lock generated
View File

@@ -85,6 +85,11 @@ dependencies = [
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "enquote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "fakegreet" name = "fakegreet"
version = "0.5.0" version = "0.5.0"
@@ -137,12 +142,12 @@ name = "greetd"
version = "0.5.0" version = "0.5.0"
dependencies = [ dependencies = [
"async-trait 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "async-trait 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"enquote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"greetd_ipc 0.5.1", "greetd_ipc 0.5.1",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pam-sys 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "pam-sys 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-ini 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -611,6 +616,7 @@ dependencies = [
"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" "checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" "checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
"checksum dlv-list 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1b391911b9a786312a10cb9d2b3d0735adfd5a8113eb3648de26a75e91b0826c" "checksum dlv-list 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1b391911b9a786312a10cb9d2b3d0735adfd5a8113eb3648de26a75e91b0826c"
"checksum enquote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ec878a5d2f3b6e9eaee72373dd23414cfc7d353104741471bec712ef241a66e"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"

View File

@@ -22,4 +22,4 @@ tokio = { version = "0.2", features = ["net", "sync", "macros", "signal", "rt-ut
getopts = "0.2" getopts = "0.2"
thiserror = "1.0" thiserror = "1.0"
async-trait = "0.1" async-trait = "0.1"
rust-ini = "0.15" enquote = "1.0.3"

134
greetd/src/config/inish.rs Normal file
View File

@@ -0,0 +1,134 @@
use std::{collections::HashMap, error::Error};
fn parse_field<'a>(line: &'a str) -> Result<(&'a str, &'a str), Box<dyn Error>> {
let split = match line.find('=') {
Some(v) => v,
None => return Err("expected equals sign on line, but found none".into()),
};
let (key, value) = line.split_at(split);
let (key, value) = (key.trim(), value.trim_matches('=').trim());
Ok((key, value))
}
type InishSection<'a> = HashMap<&'a str, &'a str>;
type Inish<'a> = HashMap<&'a str, InishSection<'a>>;
pub fn parse<'a>(s: &'a str) -> Result<Inish<'a>, Box<dyn Error>> {
let mut sections: Inish<'a> = HashMap::new();
let mut current_section = HashMap::new();
let mut current_section_name = "";
for line in s.lines() {
let line = line.trim();
let mut chars = line.chars();
let start = chars.next();
let end = chars.last();
match (start, end) {
(Some('#'), _) => continue,
(Some('['), Some(']')) => {
sections.insert(current_section_name, current_section);
current_section = HashMap::new();
let len = line.bytes().count();
current_section_name = &line[1..len - 1].trim();
}
(Some('['), v) => {
return Err(format!(
"expected Some(']') to terminate section name, but got {:?}",
v
)
.into());
}
_ if line.is_empty() => continue,
_ => {
let (key, value) = parse_field(line)?;
current_section.insert(key, value);
}
}
}
sections.insert(current_section_name, current_section);
Ok(sections)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sections() {
let config = parse(
"
[section_a]
[section_b]
",
)
.expect("config didn't parse");
if !config.contains_key("section_a") {
panic!("named section did not exist");
}
if !config.contains_key("section_b") {
panic!("named section did not exist");
}
if !config.contains_key("") {
panic!("unnamed section did not exist");
}
}
#[test]
fn fields() {
let config = parse(
"
field_outside = 'hello'
[section_a]
field_a = 1234
",
)
.expect("config didn't parse");
let section_a = match config.get("section_a") {
Some(v) => v,
None => panic!("named section did not exist"),
};
assert_eq!(section_a.get("field_a"), Some(&"1234"));
let section_unnamed = match config.get("") {
Some(v) => v,
None => panic!("unnamed section did not exist"),
};
assert_eq!(section_unnamed.get("field_outside"), Some(&"'hello'"));
}
#[test]
fn whitespaces() {
let config = parse(
"
field outside = 'hello'
[ section a ]
field a = 1234 ",
)
.expect("config didn't parse");
let section_a = match config.get("section a") {
Some(v) => v,
None => panic!("named section did not exist"),
};
assert_eq!(section_a.get("field a"), Some(&"1234"));
let section_unnamed = match config.get("") {
Some(v) => v,
None => panic!("unnamed section did not exist"),
};
assert_eq!(section_unnamed.get("field outside"), Some(&"'hello'"));
}
}

View File

@@ -1,9 +1,12 @@
use std::{env, fs::read_to_string}; use std::{collections::HashMap, env, fs::read_to_string};
use enquote::unquote;
use getopts::Options; use getopts::Options;
use super::error::Error; use super::error::Error;
mod inish;
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum VtSelection { pub enum VtSelection {
Next, Next,
@@ -48,16 +51,28 @@ fn print_usage(program: &str, opts: Options) {
println!("For more details, see greetd(1)."); println!("For more details, see greetd(1).");
} }
fn parse_old_config(config: &ini::Ini) -> Result<ConfigFile, Error> { fn maybe_unquote(s: &str) -> Result<String, Error> {
let general = config.section(None::<String>).ok_or("no general section")?; Ok(match s.chars().next() {
let greeter = general Some('"') | Some('\'') => unquote(s).map_err(|e| Error::ConfigError(format!("{}", e)))?,
_ => s.to_string(),
})
}
fn parse_old_config(config: &HashMap<&str, HashMap<&str, &str>>) -> Result<ConfigFile, Error> {
let general = config.get("").ok_or("no general section")?;
let greeterstr = general
.get("greeter") .get("greeter")
.ok_or("unable to parse configuration file: no greeter specified")?; .ok_or("unable to parse configuration file: no greeter specified")?;
let greeter_user = general.get("greeter_user").unwrap_or("greeter"); let greeter = maybe_unquote(greeterstr)?;
let vt: VtSelection = match general
let greeter_userstr = general.get("greeter_user").unwrap_or(&"greeter");
let greeter_user = maybe_unquote(greeter_userstr)?;
let vtstr = general
.get("vt") .get("vt")
.ok_or("unable to parse configuration file: no VT specified")? .ok_or("unable to parse configuration file: no VT specified")?;
{ let vt: VtSelection = match maybe_unquote(vtstr)?.as_str() {
"none" | "\"none\"" => VtSelection::None, "none" | "\"none\"" => VtSelection::None,
"current" | "\"current\"" => VtSelection::Current, "current" | "\"current\"" => VtSelection::Current,
"next" | "\"next\"" => VtSelection::Next, "next" | "\"next\"" => VtSelection::Next,
@@ -70,42 +85,56 @@ fn parse_old_config(config: &ini::Ini) -> Result<ConfigFile, Error> {
Ok(ConfigFile { Ok(ConfigFile {
terminal: ConfigTerminal { vt }, terminal: ConfigTerminal { vt },
default_session: ConfigSession { default_session: ConfigSession {
user: greeter_user.to_string(), user: greeter_user,
command: greeter.to_string(), command: greeter,
}, },
initial_session: None, initial_session: None,
}) })
} }
fn parse_new_config(config: &ini::Ini) -> Result<ConfigFile, Error> { fn parse_new_config(config: &HashMap<&str, HashMap<&str, &str>>) -> Result<ConfigFile, Error> {
let default_session = match config.section(Some("default_session")) { let default_session = match config.get("default_session") {
Some(section) => Ok(ConfigSession { Some(section) => {
command: section let commandstr = section
.get("command") .get("command")
.ok_or("default_session contains no command")? .ok_or("default_session contains no command")?;
.to_string(), let command = maybe_unquote(commandstr)
user: section.get("user").unwrap_or("greeter").to_string(), .map_err(|e| format!("unable to read default_session.command: {}", e))?;
}),
let userstr = section.get("user").unwrap_or(&"greeter");
let user = maybe_unquote(userstr)
.map_err(|e| format!("unable to read default_session.user: {}", e))?;
Ok(ConfigSession { command, user })
}
None => Err("no default_session specified"), None => Err("no default_session specified"),
}?; }?;
let initial_session = match config.section(Some("initial_session")) { let initial_session = match config.get("initial_session") {
Some(section) => Some(ConfigSession { Some(section) => {
command: section let commandstr = section
.get("command") .get("command")
.ok_or("initial_session contains no command")? .ok_or("initial_session contains no command")?;
.to_string(), let command = maybe_unquote(commandstr)
user: section .map_err(|e| format!("unable to read initial_session.command: {}", e))?;
let userstr = section
.get("user") .get("user")
.ok_or("initial_session contains no user")? .ok_or("initial_session contains no user")?;
.to_string(), let user = maybe_unquote(userstr)
}), .map_err(|e| format!("unable to read initial_session.user: {}", e))?;
Some(ConfigSession { command, user })
}
None => None, None => None,
}; };
let terminal = match config.section(Some("terminal")) { let terminal = match config.get("terminal") {
Some(section) => Ok(ConfigTerminal { Some(section) => Ok(ConfigTerminal {
vt: match section.get("vt").ok_or("VT not specified")? { vt: match maybe_unquote(section.get("vt").ok_or("VT not specified")?)
.map_err(|e| format!("unable to read terminal.vt: {}", e))?
.as_str()
{
"none" | "\"none\"" => VtSelection::None, "none" | "\"none\"" => VtSelection::None,
"current" | "\"current\"" => VtSelection::Current, "current" | "\"current\"" => VtSelection::Current,
"next" | "\"next\"" => VtSelection::Next, "next" | "\"next\"" => VtSelection::Next,
@@ -126,7 +155,7 @@ fn parse_new_config(config: &ini::Ini) -> Result<ConfigFile, Error> {
} }
fn parse_config(config_str: &str) -> Result<ConfigFile, Error> { fn parse_config(config_str: &str) -> Result<ConfigFile, Error> {
let config_ini = ini::Ini::load_from_str(config_str)?; let config_ini = inish::parse(config_str)?;
match parse_new_config(&config_ini) { match parse_new_config(&config_ini) {
Ok(v) => Ok(v), Ok(v) => Ok(v),
Err(e) => match parse_old_config(&config_ini) { Err(e) => match parse_old_config(&config_ini) {

View File

@@ -78,9 +78,3 @@ impl From<std::ffi::NulError> for Error {
Error::Error(error.to_string()) Error::Error(error.to_string())
} }
} }
impl From<ini::ini::ParseError> for Error {
fn from(error: ini::ini::ParseError) -> Self {
Error::Error(error.to_string())
}
}