roezio/config: implement Config.write. Untested

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2022-08-26 00:09:38 +02:00
parent bae5bdae98
commit 47530a5506
Signed by: pep
GPG key ID: DEDA74AEECA9D0F2
2 changed files with 50 additions and 4 deletions

View file

@ -18,6 +18,8 @@ use crate::error::Error;
use std::cell::LazyCell; use std::cell::LazyCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::fs;
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
@ -121,10 +123,45 @@ impl<'a> Config<'a> {
Ok(()) Ok(())
} }
fn write(&self, _key: &str, _value: ConfigValue, _section: &str) -> Result<(), Error> { /// Write config file with the least modification to the original file, and replace the
// TODO: Copy the file and parse it manually. If the key exists, edit it in place during /// original.
// parsing. If it doesn't, add it at the end of the section. Replace the original file fn write(&self, key: &str, value: ConfigValue, _section: &str) -> Result<(), Error> {
// atomically. // Copy the file
let tempfile = self.filename.clone().with_extension("tmp");
fs::copy(self.filename.clone(), tempfile.clone())?;
{
// Parse the file, get seek offset
let mut file = fs::File::open(tempfile.clone())?;
let mut reader = BufReader::new(file);
let mut line = String::new();
let mut clear_offset: u64 = 0;
let linestart = format!("{} ", key);
loop {
let len = reader.read_line(&mut line)?;
if line.trim().starts_with(linestart.as_str()) {
// Found offset
break;
}
clear_offset = clear_offset + u64::try_from(len)?; // This can fail?
}
// Keep remainder of the file in memory
let mut remainder = String::new();
reader.read_to_string(&mut remainder);
let mut file = reader.into_inner();
// Clear file after the found offset. If not offset was found, clears after EOF, and
// the remainder is empty.
file.set_len(clear_offset);
file.write(format!("{} = {}\n", key, value).as_bytes());
file.write(remainder.as_bytes());
}
fs::rename(tempfile, self.filename.clone());
Ok(()) Ok(())
} }

View file

@ -18,11 +18,13 @@ use crate::config::ConfigValue;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::num::TryFromIntError;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Error { pub(crate) enum Error {
IOError(io::Error), IOError(io::Error),
IntError(TryFromIntError),
UnableToCreateConfigDir, UnableToCreateConfigDir,
InvalidConfigValueType(ConfigValue), InvalidConfigValueType(ConfigValue),
InvalidValueType(String), InvalidValueType(String),
@ -33,6 +35,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Error::IOError(e) => write!(f, "io error: {}", e), Error::IOError(e) => write!(f, "io error: {}", e),
Error::IntError(e) => write!(f, "int error: {}", e),
Error::UnableToCreateConfigDir => write!(f, "Unable to create config dir"), Error::UnableToCreateConfigDir => write!(f, "Unable to create config dir"),
Error::InvalidConfigValueType(err) => write!(f, "Invalid ConfigValue type: {}", err), Error::InvalidConfigValueType(err) => write!(f, "Invalid ConfigValue type: {}", err),
Error::InvalidValueType(err) => write!(f, "Invalid value type: {}", err), Error::InvalidValueType(err) => write!(f, "Invalid value type: {}", err),
@ -50,3 +53,9 @@ impl From<io::Error> for Error {
Error::IOError(err) Error::IOError(err)
} }
} }
impl From<TryFromIntError> for Error {
fn from(err: TryFromIntError) -> Error {
Error::IntError(err)
}
}