From 34a9c3f864b0a2578dff36f6d34800b1c696ffec Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 28 Jan 2026 14:31:34 +0100 Subject: [PATCH] use interior mutability for SourceFile lines Using shared ownership for these may or may not have been a mistake in the first place. We'll find out. --- src/lib.rs | 21 +++++++--- src/main.rs | 17 +++++++- src/source.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 136 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b19be90..7a7f784 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use std::{io::BufWriter, sync::Arc}; +use std::sync::Arc; pub(crate) mod prelude { #![allow(unused_imports)] @@ -7,7 +7,7 @@ pub(crate) mod prelude { error::Error as StdError, ffi::{OsStr, OsString}, fmt::{Display, Formatter, Result as FmtResult}, - io::{Error as IoError, Read, Seek, SeekFrom}, + io::{Error as IoError, Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}, process::{Command, ExitCode}, str::FromStr, @@ -85,7 +85,6 @@ pub fn get_highest_prio( OsStr::new("; }"), ] .into_iter() - .map(ToOwned::to_owned) .collect(); // Get the highest priority, and the file its defined in. @@ -118,11 +117,21 @@ pub fn get_highest_prio( pub fn write_next_prio( mut source: SourceFile, last_line_def: SourceLine, - new_prio: u64, + last_pri: i64, + new_prio: i64, ) -> Result<(), BoxDynError> { - let lines = source.lines()?; + //let lines = source.lines()?; - for line in lines {} + let old_text = last_line_def.text(); + let new_text = old_text.replace(last_pri.to_string().as_str(), new_prio.to_string().as_str()); + + let new_line = SourceLine { + line: Line::from_index(last_line_def.line.index() + 1), + path: source.path(), + text: Arc::from(new_text), + }; + + source.insert_line(new_line.line, new_line.text())?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 75d1de1..e93d768 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,10 +35,25 @@ fn main_wrapped() -> Result<(), Box> { .custom_flags(libc::O_CLOEXEC); let source_file = SourceFile::open_from(Arc::clone(&def_path), opts)?; - let last_def_line = append_override::get_highest_prio(&args.name, source_file)?; + let last_def_line = append_override::get_highest_prio(&args.name, source_file.clone())?; + let pri: i64 = { + let text = last_def_line.text(); + let pat = "lib.mkOverride ("; + let start = text.find(pat).unwrap(); + let substr = &text[start..]; + let substr = substr.strip_prefix(pat).unwrap(); + let end_paren = substr.find(")").unwrap(); + let final_num = &substr[..end_paren]; + + final_num.parse().unwrap() + }; eprintln!("{last_def_line}"); + dbg!(&pri); + + append_override::write_next_prio(source_file, last_def_line, pri, pri - 1)?; + Ok(()) } diff --git a/src/source.rs b/src/source.rs index be70116..f5aa785 100644 --- a/src/source.rs +++ b/src/source.rs @@ -1,8 +1,10 @@ use std::{ + cell::{Cell, Ref, RefCell}, hash::Hash, - io::{BufRead, BufReader}, + io::{BufRead, BufReader, BufWriter}, + iter, mem, ops::{Deref, DerefMut}, - sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError}, + sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError, RwLock}, }; use crate::Line; @@ -19,6 +21,12 @@ pub struct SourceLine { pub text: Arc, } +impl SourceLine { + pub fn text(&self) -> Arc { + Arc::clone(&self.text) + } +} + impl Display for SourceLine { fn fmt(&self, f: &mut Formatter) -> FmtResult { write!( @@ -40,17 +48,20 @@ pub struct SourcePath { pub struct SourceFile { path: Arc, file: Arc>, - lines: Arc>>, + lines: Arc>>>, } impl SourceFile { + /// Panics if `path` is a directory path instead of a file path. pub fn open_from(path: Arc, options: OpenOptions) -> Result { + assert!(path.file_name().is_some()); + let file = Arc::new(Mutex::new(options.open(&*path)?)); Ok(Self { path, file, - lines: Arc::new(OnceLock::new()), + lines: Default::default(), }) } @@ -67,9 +78,10 @@ impl SourceFile { Ok(reader) } - pub fn lines(&self) -> Result<&[SourceLine], IoError> { + fn _lines(&self) -> Result, IoError> { if let Some(lines) = self.lines.get() { - return Ok(lines); + let as_slice = Ref::map(lines.borrow(), |lines| lines.as_slice()); + return Ok(as_slice); } let lines = BufReader::new(&*self.file.lock().unwrap()) .lines() @@ -85,9 +97,94 @@ impl SourceFile { // Mutex should have dropped by now. debug_assert!(self.file.try_lock().is_ok()); - self.lines.set(lines).unwrap(); + self.lines.set(RefCell::new(lines)).unwrap(); - Ok(self.lines.get().unwrap().as_slice()) + Ok(self._lines_slice()) + } + + pub fn lines(&self) -> Result + '_, IoError> { + self._lines() + } + + pub fn line(&self, line: Line) -> Result + '_, IoError> { + let lines_lock = self._lines()?; + let line = Ref::map(lines_lock, |lines| &lines[line.index() as usize]); + + Ok(line) + } + + /// `lines` but already be initialized. + fn _lines_slice(&self) -> Ref<'_, [SourceLine]> { + debug_assert!(self.lines.get().is_some()); + Ref::map(self.lines.get().unwrap().borrow(), |lines| lines.as_slice()) + } + + pub fn insert_line(&mut self, at: Line, text: Arc) -> Result<(), IoError> { + self.lines()?; + let path = self.path(); + + //let lines = Arc::get_mut(&mut self.lines).unwrap().get_mut().unwrap(); + let lines_guard = self.lines.get().unwrap().borrow(); + let lines = &*lines_guard; + let first_half = lines.iter().take(at.index() as usize).map(SourceLine::text); + let second_half = lines.iter().skip(at.index() as usize).map(SourceLine::text); + + let new_lines: Vec = first_half + .chain(iter::once(Arc::clone(&text))) + .chain(second_half) + .enumerate() + .map(|(idx, text)| SourceLine { + line: Line::from_index(idx as u64), + text, + path: Arc::clone(&path), + }) + .collect(); + + if cfg!(debug_assertions) { + assert_eq!(new_lines.len(), lines.len() + 1); + let newly = new_lines.get(at.index() as usize); + assert_eq!(newly.map(SourceLine::text), Some(text)); + + // Assert lines are continuous. + let linenrs: Vec = new_lines + .iter() + .map(|source_line| source_line.line) + .collect(); + assert!(linenrs.is_sorted()); + } + + // Write it to a file in the same directory. + let new_name: OsString = [ + // foo + path.file_name().unwrap(), + OsStr::new(".tmp"), + ] + .into_iter() + .collect::(); + let tmp_path = path.with_file_name(&new_name); + let tmp_file = File::options() + .create(true) + .write(true) + .truncate(true) + .custom_flags(libc::O_EXCL | libc::O_CLOEXEC) + .open(&tmp_path)?; + + let mut writer = BufWriter::new(tmp_file); + for line in new_lines.iter() { + writer.write_all(line.text().as_bytes())?; + writer.write_all(b"\n")?; + } + writer.flush()?; + drop(writer); + // Rename the temporary file to the new file, which is atomic (TODO: I think). + fs_err::rename(&tmp_path, &path)?; + + drop(lines_guard); + let mut lines_guard = self.lines.get().unwrap().borrow_mut(); + // Finally, update state. + let _old_lines = mem::replace(&mut *lines_guard, new_lines); + + Ok(()) } pub fn path(&self) -> Arc {