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.
This commit is contained in:
parent
e5d0bdf0c0
commit
34a9c3f864
21
src/lib.rs
21
src/lib.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{io::BufWriter, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) mod prelude {
|
pub(crate) mod prelude {
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
@ -7,7 +7,7 @@ pub(crate) mod prelude {
|
||||||
error::Error as StdError,
|
error::Error as StdError,
|
||||||
ffi::{OsStr, OsString},
|
ffi::{OsStr, OsString},
|
||||||
fmt::{Display, Formatter, Result as FmtResult},
|
fmt::{Display, Formatter, Result as FmtResult},
|
||||||
io::{Error as IoError, Read, Seek, SeekFrom},
|
io::{Error as IoError, Read, Seek, SeekFrom, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, ExitCode},
|
process::{Command, ExitCode},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
|
@ -85,7 +85,6 @@ pub fn get_highest_prio(
|
||||||
OsStr::new("; }"),
|
OsStr::new("; }"),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(ToOwned::to_owned)
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Get the highest priority, and the file its defined in.
|
// Get the highest priority, and the file its defined in.
|
||||||
|
|
@ -118,11 +117,21 @@ pub fn get_highest_prio(
|
||||||
pub fn write_next_prio(
|
pub fn write_next_prio(
|
||||||
mut source: SourceFile,
|
mut source: SourceFile,
|
||||||
last_line_def: SourceLine,
|
last_line_def: SourceLine,
|
||||||
new_prio: u64,
|
last_pri: i64,
|
||||||
|
new_prio: i64,
|
||||||
) -> Result<(), BoxDynError> {
|
) -> 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
17
src/main.rs
17
src/main.rs
|
|
@ -35,10 +35,25 @@ fn main_wrapped() -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
|
||||||
.custom_flags(libc::O_CLOEXEC);
|
.custom_flags(libc::O_CLOEXEC);
|
||||||
let source_file = SourceFile::open_from(Arc::clone(&def_path), opts)?;
|
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}");
|
eprintln!("{last_def_line}");
|
||||||
|
|
||||||
|
dbg!(&pri);
|
||||||
|
|
||||||
|
append_override::write_next_prio(source_file, last_def_line, pri, pri - 1)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
113
src/source.rs
113
src/source.rs
|
|
@ -1,8 +1,10 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
cell::{Cell, Ref, RefCell},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader, BufWriter},
|
||||||
|
iter, mem,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError},
|
sync::{Arc, Mutex, MutexGuard, OnceLock, PoisonError, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Line;
|
use crate::Line;
|
||||||
|
|
@ -19,6 +21,12 @@ pub struct SourceLine {
|
||||||
pub text: Arc<str>,
|
pub text: Arc<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SourceLine {
|
||||||
|
pub fn text(&self) -> Arc<str> {
|
||||||
|
Arc::clone(&self.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for SourceLine {
|
impl Display for SourceLine {
|
||||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||||
write!(
|
write!(
|
||||||
|
|
@ -40,17 +48,20 @@ pub struct SourcePath {
|
||||||
pub struct SourceFile {
|
pub struct SourceFile {
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
file: Arc<Mutex<File>>,
|
file: Arc<Mutex<File>>,
|
||||||
lines: Arc<OnceLock<Vec<SourceLine>>>,
|
lines: Arc<OnceLock<RefCell<Vec<SourceLine>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceFile {
|
impl SourceFile {
|
||||||
|
/// Panics if `path` is a directory path instead of a file path.
|
||||||
pub fn open_from(path: Arc<Path>, options: OpenOptions) -> Result<Self, IoError> {
|
pub fn open_from(path: Arc<Path>, options: OpenOptions) -> Result<Self, IoError> {
|
||||||
|
assert!(path.file_name().is_some());
|
||||||
|
|
||||||
let file = Arc::new(Mutex::new(options.open(&*path)?));
|
let file = Arc::new(Mutex::new(options.open(&*path)?));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path,
|
path,
|
||||||
file,
|
file,
|
||||||
lines: Arc::new(OnceLock::new()),
|
lines: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,9 +78,10 @@ impl SourceFile {
|
||||||
Ok(reader)
|
Ok(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lines(&self) -> Result<&[SourceLine], IoError> {
|
fn _lines(&self) -> Result<Ref<'_, [SourceLine]>, IoError> {
|
||||||
if let Some(lines) = self.lines.get() {
|
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())
|
let lines = BufReader::new(&*self.file.lock().unwrap())
|
||||||
.lines()
|
.lines()
|
||||||
|
|
@ -85,9 +97,94 @@ impl SourceFile {
|
||||||
// Mutex should have dropped by now.
|
// Mutex should have dropped by now.
|
||||||
debug_assert!(self.file.try_lock().is_ok());
|
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<impl Deref<Target = [SourceLine]> + '_, IoError> {
|
||||||
|
self._lines()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line(&self, line: Line) -> Result<impl Deref<Target = SourceLine> + '_, 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<str>) -> 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<SourceLine> = 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<Line> = 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::<OsString>();
|
||||||
|
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<Path> {
|
pub fn path(&self) -> Arc<Path> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue