config: Replace rust-ini
This cuts binary size by around 30KiB on my local release builds.
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -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"
|
||||||
|
@@ -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
134
greetd/src/config/inish.rs
Normal 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'"));
|
||||||
|
}
|
||||||
|
}
|
@@ -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) {
|
||||||
|
@@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user