Add simple (if ugly) settings dialog
This commit is contained in:
parent
fcb997bfb3
commit
6420278f71
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -308,6 +308,17 @@ dependencies = [
|
|||||||
"shell-words",
|
"shell-words",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bscreensaver-settings"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bscreensaver-util",
|
||||||
|
"glib",
|
||||||
|
"gtk",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bscreensaver-systemd"
|
name = "bscreensaver-systemd"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -6,6 +6,7 @@ members = [
|
|||||||
"dbus-service",
|
"dbus-service",
|
||||||
"dialog-gtk3",
|
"dialog-gtk3",
|
||||||
"util",
|
"util",
|
||||||
|
"settings",
|
||||||
"systemd",
|
"systemd",
|
||||||
"xcb-xembed",
|
"xcb-xembed",
|
||||||
]
|
]
|
||||||
|
9
Makefile
9
Makefile
@ -4,7 +4,9 @@ PREFIX ?= /usr/local
|
|||||||
BINDIR ?= $(PREFIX)/bin
|
BINDIR ?= $(PREFIX)/bin
|
||||||
LIBEXECDIR ?= $(PREFIX)/libexec
|
LIBEXECDIR ?= $(PREFIX)/libexec
|
||||||
SYSCONFDIR ?= $(PREFIX)/etc
|
SYSCONFDIR ?= $(PREFIX)/etc
|
||||||
|
DATADIR ?= $(PREFIX)/share
|
||||||
|
|
||||||
|
APPLICATIONS_DIR = $(DATADIR)/applications
|
||||||
CONFIG_DIR = $(SYSCONFDIR)/xdg/bscreensaver
|
CONFIG_DIR = $(SYSCONFDIR)/xdg/bscreensaver
|
||||||
HELPER_DIR = $(LIBEXECDIR)/bscreensaver
|
HELPER_DIR = $(LIBEXECDIR)/bscreensaver
|
||||||
HELPERS = \
|
HELPERS = \
|
||||||
@ -26,16 +28,17 @@ dev:
|
|||||||
HELPER_DIR=target/debug cargo build
|
HELPER_DIR=target/debug cargo build
|
||||||
|
|
||||||
install: release
|
install: release
|
||||||
$(INSTALL) -m 0755 -d $(BINDIR) $(HELPER_DIR) $(CONFIG_DIR)
|
$(INSTALL) -m 0755 -d $(BINDIR) $(HELPER_DIR) $(CONFIG_DIR) $(APPLICATIONS_DIR)
|
||||||
$(INSTALL) -m 0755 target/release/bscreensaver target/release/bscreensaver-command $(BINDIR)
|
$(INSTALL) -m 0755 target/release/bscreensaver target/release/bscreensaver-command target/release/bscreensaver-settings $(BINDIR)
|
||||||
$(INSTALL) -m 0755 $(addprefix target/release/,$(HELPERS)) $(HELPER_DIR)
|
$(INSTALL) -m 0755 $(addprefix target/release/,$(HELPERS)) $(HELPER_DIR)
|
||||||
$(INSTALL) -m 0644 bscreensaver.toml.example $(CONFIG_DIR)
|
$(INSTALL) -m 0644 bscreensaver.toml.example $(CONFIG_DIR)
|
||||||
|
$(INSTALL) -m 0644 settings/bscreensaver-settings.desktop $(APPLICATIONS_DIR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
cargo clean
|
cargo clean
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f $(BINDIR)/bscreensaver $(BINDIR)/bscreensaver-command $(addprefix $(HELPER_DIR)/,$(HELPERS)) || true
|
rm -f $(BINDIR)/bscreensaver $(BINDIR)/bscreensaver-command $(BINDIR)/bscreensaver-settings $(addprefix $(HELPER_DIR)/,$(HELPERS)) || true
|
||||||
rmdir -p $(BINDIR) $(HELPER_DIR) || true
|
rmdir -p $(BINDIR) $(HELPER_DIR) || true
|
||||||
rmdir -p $(PREFIX) || true
|
rmdir -p $(PREFIX) || true
|
||||||
|
|
||||||
|
11
settings/Cargo.toml
Normal file
11
settings/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "bscreensaver-settings"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
bscreensaver-util = { path = "../util" }
|
||||||
|
glib = { version = "0.15", features = ["v2_68"] }
|
||||||
|
gtk = { version = "0.15", features = ["v3_24"] }
|
||||||
|
log = "0.4"
|
10
settings/bscreensaver-settings.desktop
Normal file
10
settings/bscreensaver-settings.desktop
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=Screensaver Settings
|
||||||
|
Comment=Customize screensaver settings
|
||||||
|
Exec=bscreensaver-settings
|
||||||
|
Icon=screensaver
|
||||||
|
Terminal=false
|
||||||
|
StartupNotify=true
|
||||||
|
Type=Application
|
||||||
|
Categories=GTK;Settings;DesktopSettings;
|
249
settings/src/main.rs
Normal file
249
settings/src/main.rs
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
use gtk::{glib, prelude::*};
|
||||||
|
use glib::clone;
|
||||||
|
use std::{env, process::exit, time::Duration};
|
||||||
|
|
||||||
|
use bscreensaver_util::settings::Configuration;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Widgets {
|
||||||
|
lock_timeout: gtk::SpinButton,
|
||||||
|
blank_before_locking: gtk::SpinButton,
|
||||||
|
new_login_command: gtk::Entry,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let config = Configuration::load()?;
|
||||||
|
|
||||||
|
let app = gtk::Application::builder()
|
||||||
|
.application_id("org.spurint.bscreensaver-settings")
|
||||||
|
.build();
|
||||||
|
app.connect_activate(move |app| show_ui(&app, &config));
|
||||||
|
|
||||||
|
exit(app.run_with_args(&env::args().into_iter().collect::<Vec<String>>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_ui(app: >k::Application, config: &Configuration) {
|
||||||
|
let mainwin = gtk::ApplicationWindow::builder()
|
||||||
|
.application(app)
|
||||||
|
.name("BScreensaver Settings")
|
||||||
|
.title("Screensaver Settings")
|
||||||
|
.type_(gtk::WindowType::Toplevel)
|
||||||
|
.resizable(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let label_sg = gtk::SizeGroup::builder()
|
||||||
|
.mode(gtk::SizeGroupMode::Horizontal)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let topvbox = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.spacing(8)
|
||||||
|
.margin(4)
|
||||||
|
.build();
|
||||||
|
mainwin.add(&topvbox);
|
||||||
|
|
||||||
|
let hbox = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Horizontal)
|
||||||
|
.spacing(8)
|
||||||
|
.build();
|
||||||
|
topvbox.pack_start(&hbox, false, false, 0);
|
||||||
|
|
||||||
|
let label = gtk::Label::builder()
|
||||||
|
.label("Lock screen after")
|
||||||
|
.xalign(0.0)
|
||||||
|
.build();
|
||||||
|
label_sg.add_widget(&label);
|
||||||
|
hbox.pack_start(&label, false, false, 0);
|
||||||
|
|
||||||
|
let lock_timeout_spinbutton = gtk::SpinButton::builder()
|
||||||
|
.adjustment(>k::Adjustment::builder()
|
||||||
|
.lower(1.0)
|
||||||
|
.upper(f64::MAX as u32 as f64)
|
||||||
|
.step_increment(1.0)
|
||||||
|
.page_increment(5.0)
|
||||||
|
.value((config.lock_timeout.as_secs() / 60) as f64)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.tooltip_text("Minutes before the screen locks")
|
||||||
|
.activates_default(true)
|
||||||
|
.build();
|
||||||
|
hbox.pack_start(&lock_timeout_spinbutton, false, false, 0);
|
||||||
|
|
||||||
|
let label = gtk::Label::builder()
|
||||||
|
.label("minutes")
|
||||||
|
.xalign(0.0)
|
||||||
|
.build();
|
||||||
|
hbox.pack_start(&label, false, false, 0);
|
||||||
|
|
||||||
|
let hbox = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Horizontal)
|
||||||
|
.spacing(8)
|
||||||
|
.build();
|
||||||
|
topvbox.pack_start(&hbox, false, false, 0);
|
||||||
|
|
||||||
|
let label = gtk::Label::builder()
|
||||||
|
.label("Blank screen")
|
||||||
|
.xalign(0.0)
|
||||||
|
.build();
|
||||||
|
label_sg.add_widget(&label);
|
||||||
|
hbox.pack_start(&label, false, false, 0);
|
||||||
|
|
||||||
|
let blank_before_locking_spinbutton = gtk::SpinButton::builder()
|
||||||
|
.adjustment(>k::Adjustment::builder()
|
||||||
|
.lower(0.0)
|
||||||
|
.upper(f64::MAX as u32 as f64)
|
||||||
|
.step_increment(1.0)
|
||||||
|
.page_increment(5.0)
|
||||||
|
.value((config.blank_before_locking.as_secs() / 60) as f64)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.tooltip_text("Minutes before screen locks to blank the screen")
|
||||||
|
.activates_default(true)
|
||||||
|
.build();
|
||||||
|
hbox.pack_start(&blank_before_locking_spinbutton, false, false, 0);
|
||||||
|
|
||||||
|
let label = gtk::Label::builder()
|
||||||
|
.label("minutes before locking")
|
||||||
|
.xalign(0.0)
|
||||||
|
.build();
|
||||||
|
hbox.pack_start(&label, false, false, 0);
|
||||||
|
|
||||||
|
let hbox = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Horizontal)
|
||||||
|
.spacing(8)
|
||||||
|
.build();
|
||||||
|
topvbox.pack_start(&hbox, false, false, 0);
|
||||||
|
|
||||||
|
let label = gtk::Label::builder()
|
||||||
|
.label("New Login Command:")
|
||||||
|
.tooltip_text("Command to run when the 'New Login' button is clicked in the unlock dialog")
|
||||||
|
.xalign(0.0)
|
||||||
|
.build();
|
||||||
|
label_sg.add_widget(&label);
|
||||||
|
hbox.pack_start(&label, false, false, 0);
|
||||||
|
|
||||||
|
let new_login_command_entry = gtk::Entry::builder()
|
||||||
|
.text(config.new_login_command.as_ref().unwrap_or(&"".to_string()))
|
||||||
|
.width_chars(30)
|
||||||
|
.activates_default(true)
|
||||||
|
.build();
|
||||||
|
hbox.pack_start(&new_login_command_entry, false, false, 0);
|
||||||
|
|
||||||
|
let widgets = Widgets {
|
||||||
|
lock_timeout: lock_timeout_spinbutton.clone(),
|
||||||
|
blank_before_locking: blank_before_locking_spinbutton.clone(),
|
||||||
|
new_login_command: new_login_command_entry.clone(),
|
||||||
|
};
|
||||||
|
mainwin.connect_delete_event(clone!(@strong config, @strong widgets, @strong app, @strong mainwin => move |_,_| {
|
||||||
|
Inhibit(!confirm_cancel(&config, &widgets, &mainwin))
|
||||||
|
}));
|
||||||
|
|
||||||
|
let button = gtk::Button::from_icon_name(Some("folder-open"), gtk::IconSize::Button);
|
||||||
|
button.connect_clicked(clone!(@strong mainwin, @strong widgets => move |_| {
|
||||||
|
run_file_chooser(&mainwin, &widgets);
|
||||||
|
}));
|
||||||
|
hbox.pack_start(&button, false, false, 0);
|
||||||
|
|
||||||
|
let button_box = gtk::ButtonBox::builder()
|
||||||
|
.spacing(8)
|
||||||
|
.layout_style(gtk::ButtonBoxStyle::End)
|
||||||
|
.build();
|
||||||
|
topvbox.pack_end(&button_box, false, false, 0);
|
||||||
|
|
||||||
|
let close_button = gtk::Button::builder()
|
||||||
|
.label("Close")
|
||||||
|
.build();
|
||||||
|
close_button.connect_clicked(clone!(@strong config, @strong widgets, @strong app, @strong mainwin => move |_| {
|
||||||
|
if confirm_cancel(&config, &widgets, &mainwin) {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
button_box.pack_end(&close_button, false, false, 0);
|
||||||
|
|
||||||
|
let save_button = gtk::Button::builder()
|
||||||
|
.label("Save")
|
||||||
|
.build();
|
||||||
|
save_button.connect_clicked(clone!(@strong config, @strong widgets, @strong app, @strong mainwin => move |_| {
|
||||||
|
if save_config(&config, &widgets, &mainwin) {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
button_box.pack_end(&save_button, false, false, 0);
|
||||||
|
save_button.set_can_default(true);
|
||||||
|
save_button.set_has_default(true);
|
||||||
|
|
||||||
|
mainwin.show_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_file_chooser(mainwin: >k::ApplicationWindow, widgets: &Widgets) {
|
||||||
|
let file_chooser = gtk::FileChooserNative::new(
|
||||||
|
Some("New Login Command"),
|
||||||
|
Some(mainwin),
|
||||||
|
gtk::FileChooserAction::Open,
|
||||||
|
Some("Select"),
|
||||||
|
Some("Cancel"),
|
||||||
|
);
|
||||||
|
let response = file_chooser.run();
|
||||||
|
file_chooser.hide();
|
||||||
|
if response == gtk::ResponseType::Accept {
|
||||||
|
if let Some(filename) = file_chooser.filename() {
|
||||||
|
widgets.new_login_command.set_text(&filename.to_string_lossy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_config(config: &Configuration, widgets: &Widgets, mainwin: >k::ApplicationWindow) -> bool{
|
||||||
|
let (new_config, changed) = build_new_configuration(&config, &widgets);
|
||||||
|
if changed {
|
||||||
|
if let Err(err) = new_config.save() {
|
||||||
|
let error_dialog = gtk::MessageDialog::new(
|
||||||
|
Some(mainwin),
|
||||||
|
gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT,
|
||||||
|
gtk::MessageType::Error,
|
||||||
|
gtk::ButtonsType::Close,
|
||||||
|
&format!("Failed to save configuration: {}", err),
|
||||||
|
);
|
||||||
|
error_dialog.set_title("Screensaver Settings");
|
||||||
|
error_dialog.run();
|
||||||
|
error_dialog.hide();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm_cancel(config: &Configuration, widgets: &Widgets, mainwin: >k::ApplicationWindow) -> bool {
|
||||||
|
let (_, changed) = build_new_configuration(&config, &widgets);
|
||||||
|
if changed {
|
||||||
|
let dialog = gtk::MessageDialog::new(
|
||||||
|
Some(mainwin),
|
||||||
|
gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT,
|
||||||
|
gtk::MessageType::Warning,
|
||||||
|
gtk::ButtonsType::None,
|
||||||
|
"You have unsaved changes. Are you sure you want to close?",
|
||||||
|
);
|
||||||
|
dialog.set_title("Screensaver Settings");
|
||||||
|
dialog.add_buttons(&[
|
||||||
|
("Close Window", gtk::ResponseType::Close),
|
||||||
|
("Cancel", gtk::ResponseType::Cancel),
|
||||||
|
]);
|
||||||
|
dialog.set_default_response(gtk::ResponseType::Cancel);
|
||||||
|
let response = dialog.run();
|
||||||
|
dialog.hide();
|
||||||
|
match response {
|
||||||
|
gtk::ResponseType::Close => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_new_configuration(old_config: &Configuration, widgets: &Widgets) -> (Configuration, bool) {
|
||||||
|
let mut new_config = old_config.clone();
|
||||||
|
new_config.lock_timeout = Duration::from_secs(widgets.lock_timeout.adjustment().value() as u64 * 60);
|
||||||
|
new_config.blank_before_locking = Duration::from_secs(widgets.blank_before_locking.adjustment().value() as u64 * 60);
|
||||||
|
new_config.new_login_command = Some(widgets.new_login_command.text()).filter(|s| !s.is_empty()).map(|s| s.to_string());
|
||||||
|
let changed = old_config != &new_config;
|
||||||
|
(new_config, changed)
|
||||||
|
}
|
@ -1,8 +1,15 @@
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::{fs::File, io::Read, path::PathBuf, time::Duration};
|
use std::{fmt, fs::{self, File}, io::{Read, Write}, path::PathBuf, time::Duration};
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
const CONFIG_FILE_RELATIVE_PATH: &str = "bscreensaver/bscreensaver.toml";
|
||||||
|
|
||||||
|
const LOCK_TIMEOUT: &str = "lock-timeout";
|
||||||
|
const BLANK_BEFORE_LOCKING: &str = "blank-before-locking";
|
||||||
|
const DIALOG_BACKEND: &str = "dialog-backend";
|
||||||
|
const NEW_LOGIN_COMMAND: &str = "new-login-command";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum DialogBackend {
|
pub enum DialogBackend {
|
||||||
Gtk3,
|
Gtk3,
|
||||||
}
|
}
|
||||||
@ -13,6 +20,20 @@ impl DialogBackend {
|
|||||||
Self::Gtk3 => "bscreensaver-dialog-gtk3",
|
Self::Gtk3 => "bscreensaver-dialog-gtk3",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Gtk3 => "GTK3",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DialogBackend {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Gtk3 => write!(f, "gtk3"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for DialogBackend {
|
impl TryFrom<&str> for DialogBackend {
|
||||||
@ -25,7 +46,7 @@ impl TryFrom<&str> for DialogBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub lock_timeout: Duration,
|
pub lock_timeout: Duration,
|
||||||
pub blank_before_locking: Duration,
|
pub blank_before_locking: Duration,
|
||||||
@ -38,7 +59,7 @@ impl Configuration {
|
|||||||
use humantime::parse_duration;
|
use humantime::parse_duration;
|
||||||
|
|
||||||
let config_tomls = xdg::BaseDirectories::new()?
|
let config_tomls = xdg::BaseDirectories::new()?
|
||||||
.find_config_files("bscreensaver/bscreensaver.toml")
|
.find_config_files(CONFIG_FILE_RELATIVE_PATH)
|
||||||
.map(|config_path| parse_config_toml(&config_path))
|
.map(|config_path| parse_config_toml(&config_path))
|
||||||
.collect::<Result<Vec<Value>, _>>()?;
|
.collect::<Result<Vec<Value>, _>>()?;
|
||||||
|
|
||||||
@ -69,16 +90,30 @@ impl Configuration {
|
|||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_config_toml(config_path: &PathBuf) -> anyhow::Result<Value> {
|
pub fn save(&self) -> anyhow::Result<()> {
|
||||||
let mut f = File::open(config_path)?;
|
use humantime::format_duration;
|
||||||
let mut config = String::new();
|
use toml::map::Map;
|
||||||
f.read_to_string(&mut config)?;
|
|
||||||
|
let mut config_map = Map::new();
|
||||||
|
config_map.insert(LOCK_TIMEOUT.to_string(), Value::String(format_duration(self.lock_timeout).to_string()));
|
||||||
|
config_map.insert(BLANK_BEFORE_LOCKING.to_string(), Value::String(format_duration(self.blank_before_locking).to_string()));
|
||||||
|
config_map.insert(DIALOG_BACKEND.to_string(), Value::String(self.dialog_backend.to_string()));
|
||||||
|
if let Some(new_login_command) = &self.new_login_command {
|
||||||
|
config_map.insert(NEW_LOGIN_COMMAND.to_string(), Value::String(new_login_command.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_path = xdg::BaseDirectories::new()?.place_config_file(CONFIG_FILE_RELATIVE_PATH)?;
|
||||||
|
let mut tmp_filename = config_path.file_name().unwrap().to_os_string();
|
||||||
|
tmp_filename.push(".new");
|
||||||
|
let tmp_path = config_path.with_file_name(tmp_filename);
|
||||||
|
let mut f = File::create(&tmp_path)?;
|
||||||
|
f.write_all(Value::Table(config_map).to_string().as_bytes())?;
|
||||||
drop(f);
|
drop(f);
|
||||||
|
fs::rename(tmp_path, config_path)?;
|
||||||
|
|
||||||
let config_toml = config.parse::<Value>()?;
|
Ok(())
|
||||||
Ok(config_toml)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Configuration {
|
impl Default for Configuration {
|
||||||
@ -92,3 +127,12 @@ impl Default for Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_config_toml(config_path: &PathBuf) -> anyhow::Result<Value> {
|
||||||
|
let mut f = File::open(config_path)?;
|
||||||
|
let mut config = String::new();
|
||||||
|
f.read_to_string(&mut config)?;
|
||||||
|
drop(f);
|
||||||
|
|
||||||
|
let config_toml = config.parse::<Value>()?;
|
||||||
|
Ok(config_toml)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user