PoC
This commit is contained in:
parent
34a9c3f864
commit
551e5a7851
74
src/lib.rs
74
src/lib.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
pub(crate) mod prelude {
|
pub(crate) mod prelude {
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
@ -43,6 +43,8 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::source::SourceFile;
|
use crate::source::SourceFile;
|
||||||
|
|
||||||
|
pub const ASCII_WHITESPACE: &[char] = &['\t', '\n', '\x0C', '\r', ' '];
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
pub struct DefinitionWithLocation {
|
pub struct DefinitionWithLocation {
|
||||||
pub file: Box<Path>,
|
pub file: Box<Path>,
|
||||||
|
|
@ -75,8 +77,8 @@ pub fn get_where(option_name: &str, configuration_nix: &Path) -> Result<Box<Path
|
||||||
|
|
||||||
pub fn get_highest_prio(
|
pub fn get_highest_prio(
|
||||||
option_name: &str,
|
option_name: &str,
|
||||||
mut source: SourceFile,
|
source: SourceFile,
|
||||||
) -> Result<SourceLine, BoxDynError> {
|
) -> Result<(i64, SourceLine), BoxDynError> {
|
||||||
// Get the current highest priority.
|
// Get the current highest priority.
|
||||||
|
|
||||||
let expr: OsString = [
|
let expr: OsString = [
|
||||||
|
|
@ -93,7 +95,7 @@ pub fn get_highest_prio(
|
||||||
.into_command()
|
.into_command()
|
||||||
.output_checked_utf8()?;
|
.output_checked_utf8()?;
|
||||||
let stdout = output.stdout();
|
let stdout = output.stdout();
|
||||||
let highest_prio = stdout.trim();
|
let highest_prio = i64::from_str(stdout.trim())?;
|
||||||
|
|
||||||
let needle = format!("lib.mkOverride ({})", highest_prio);
|
let needle = format!("lib.mkOverride ({})", highest_prio);
|
||||||
|
|
||||||
|
|
@ -111,27 +113,77 @@ pub fn get_highest_prio(
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(line.clone())
|
Ok((highest_prio, line.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_next_prio_line(
|
||||||
|
source: SourceFile,
|
||||||
|
option_name: Arc<str>,
|
||||||
|
last_line_def: SourceLine,
|
||||||
|
new_prio: i64,
|
||||||
|
new_value: Arc<str>,
|
||||||
|
) -> Result<SourceLine, BoxDynError> {
|
||||||
|
if !last_line_def.text.ends_with(';') {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
let next_line = source.line(last_line_def.line.next())?;
|
||||||
|
if next_line.text.trim() != "}" {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (indentation, _rest) = last_line_def.text.split_at(
|
||||||
|
last_line_def
|
||||||
|
.text
|
||||||
|
.find(|ch: char| !ch.is_ascii_whitespace())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
// FIXME: fix indentation
|
||||||
|
let new_text = format!("{indentation}{option_name} = lib.mkOverride ({new_prio}) ({new_value});",);
|
||||||
|
|
||||||
|
let new_line = SourceLine {
|
||||||
|
line: next_line.line.next(),
|
||||||
|
path: source.path(),
|
||||||
|
text: Arc::from(new_text),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(new_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_next_prio(
|
pub fn write_next_prio(
|
||||||
mut source: SourceFile,
|
mut source: SourceFile,
|
||||||
last_line_def: SourceLine,
|
last_line_def: SourceLine,
|
||||||
last_pri: i64,
|
new_text: Arc<str>,
|
||||||
new_prio: i64,
|
|
||||||
) -> Result<(), BoxDynError> {
|
) -> Result<(), BoxDynError> {
|
||||||
//let lines = source.lines()?;
|
//let lines = source.lines()?;
|
||||||
|
|
||||||
let old_text = last_line_def.text();
|
let open_brace_line = source.line(last_line_def.line.prev())?.text();
|
||||||
let new_text = old_text.replace(last_pri.to_string().as_str(), new_prio.to_string().as_str());
|
let close_brace_line = source.line(last_line_def.line.next())?.text();
|
||||||
|
|
||||||
|
let new_mod_start = SourceLine {
|
||||||
|
line: last_line_def.line.next(),
|
||||||
|
path: source.path(),
|
||||||
|
text: open_brace_line,
|
||||||
|
};
|
||||||
let new_line = SourceLine {
|
let new_line = SourceLine {
|
||||||
line: Line::from_index(last_line_def.line.index() + 1),
|
line: new_mod_start.line.next(),
|
||||||
path: source.path(),
|
path: source.path(),
|
||||||
text: Arc::from(new_text),
|
text: Arc::from(new_text),
|
||||||
};
|
};
|
||||||
|
let new_mod_end = SourceLine {
|
||||||
|
line: new_line.line.next(),
|
||||||
|
path: source.path(),
|
||||||
|
text: close_brace_line,
|
||||||
|
};
|
||||||
|
|
||||||
source.insert_line(new_line.line, new_line.text())?;
|
dbg!(&new_mod_start.text());
|
||||||
|
|
||||||
|
source.insert_lines(&[
|
||||||
|
new_mod_start,
|
||||||
|
new_line,
|
||||||
|
new_mod_end,
|
||||||
|
])?;
|
||||||
|
|
||||||
|
//source.insert_line(new_line.line, new_line.text())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
src/line.rs
19
src/line.rs
|
|
@ -9,24 +9,35 @@ pub struct Line(pub u64);
|
||||||
|
|
||||||
/// Constructors.
|
/// Constructors.
|
||||||
impl Line {
|
impl Line {
|
||||||
pub fn from_index(index: u64) -> Self {
|
pub const fn from_index(index: u64) -> Self {
|
||||||
Self(index)
|
Self(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_linenr(linenr: NonZeroU64) -> Self {
|
pub const fn from_linenr(linenr: NonZeroU64) -> Self {
|
||||||
Self(linenr.get() - 1)
|
Self(linenr.get() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn next(self) -> Self {
|
||||||
|
Self::from_index(self.index() + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panics if self is line index 0.
|
||||||
|
pub const fn prev(self) -> Self {
|
||||||
|
Self::from_index(self.index() - 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Getters.
|
/// Getters.
|
||||||
impl Line {
|
impl Line {
|
||||||
/// 0-indexed
|
/// 0-indexed
|
||||||
pub fn index(self) -> u64 {
|
pub const fn index(self) -> u64 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 1-indexed
|
/// 1-indexed
|
||||||
pub fn linenr(self) -> u64 {
|
pub const fn linenr(self) -> u64 {
|
||||||
self.0 + 1
|
self.0 + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Lines(Vec<Line>);
|
||||||
|
|
|
||||||
32
src/main.rs
32
src/main.rs
|
|
@ -1,8 +1,7 @@
|
||||||
use std::{error::Error as StdError, sync::Arc};
|
|
||||||
use std::io::{self, IsTerminal};
|
use std::io::{self, IsTerminal};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
use std::{error::Error as StdError, sync::Arc};
|
||||||
|
|
||||||
use append_override::source::SourceFile;
|
use append_override::source::SourceFile;
|
||||||
use clap::{ColorChoice, Parser as _};
|
use clap::{ColorChoice, Parser as _};
|
||||||
|
|
@ -28,31 +27,28 @@ fn main_wrapped() -> Result<(), Box<dyn StdError + Send + Sync + 'static>> {
|
||||||
let def_path = append_override::get_where(&args.name, filepath)?;
|
let def_path = append_override::get_where(&args.name, filepath)?;
|
||||||
let def_path = Arc::from(def_path);
|
let def_path = Arc::from(def_path);
|
||||||
let mut opts = File::options();
|
let mut opts = File::options();
|
||||||
opts
|
opts.read(true)
|
||||||
.read(true)
|
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(false)
|
.create(false)
|
||||||
.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.clone())?;
|
let (pri, last_def_line) = append_override::get_highest_prio(&args.name, source_file.clone())?;
|
||||||
let pri: i64 = {
|
let new_pri = pri - 1;
|
||||||
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);
|
let new_pri_line = append_override::get_next_prio_line(
|
||||||
|
source_file.clone(),
|
||||||
|
args.name.into(),
|
||||||
|
last_def_line.clone(),
|
||||||
|
new_pri,
|
||||||
|
args.value.into(),
|
||||||
|
)?;
|
||||||
|
|
||||||
append_override::write_next_prio(source_file, last_def_line, pri, pri - 1)?;
|
eprintln!("new_pri_line={new_pri_line}");
|
||||||
|
|
||||||
|
append_override::write_next_prio(source_file, last_def_line, new_pri_line.text())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ pub struct SourcePath {
|
||||||
pub struct SourceFile {
|
pub struct SourceFile {
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
file: Arc<Mutex<File>>,
|
file: Arc<Mutex<File>>,
|
||||||
|
/// References to `SourceFile` do not prevent mutating `lines`.
|
||||||
|
/// Also `lines` is lazily initialized.
|
||||||
lines: Arc<OnceLock<RefCell<Vec<SourceLine>>>>,
|
lines: Arc<OnceLock<RefCell<Vec<SourceLine>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +121,75 @@ impl SourceFile {
|
||||||
Ref::map(self.lines.get().unwrap().borrow(), |lines| lines.as_slice())
|
Ref::map(self.lines.get().unwrap().borrow(), |lines| lines.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// With debug assertions, panics if `lines` are not contiguous.
|
||||||
|
pub fn insert_lines(&mut self, new_lines: &[SourceLine]) -> Result<(), IoError> {
|
||||||
|
if new_lines.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(new_lines.is_sorted_by(|lhs, rhs| lhs.line.next() == rhs.line));
|
||||||
|
|
||||||
|
let path = self.path();
|
||||||
|
let cur_lines = self.lines()?;
|
||||||
|
let first_half = cur_lines
|
||||||
|
.iter()
|
||||||
|
.take(new_lines.last().unwrap().line.prev().index() as usize)
|
||||||
|
.map(SourceLine::text);
|
||||||
|
let middle = new_lines.iter().map(SourceLine::text);
|
||||||
|
let second_half = cur_lines
|
||||||
|
.iter()
|
||||||
|
.skip(new_lines.last().unwrap().line.prev().index() as usize)
|
||||||
|
.map(SourceLine::text);
|
||||||
|
|
||||||
|
let final_lines: Vec<SourceLine> = first_half
|
||||||
|
.chain(middle)
|
||||||
|
.chain(second_half)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, text)| SourceLine {
|
||||||
|
line: Line::from_index(idx as u64),
|
||||||
|
text,
|
||||||
|
path: self.path(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Assert lines are continuous.
|
||||||
|
debug_assert!(final_lines.is_sorted_by(|lhs, rhs| lhs.line.next() == rhs.line));
|
||||||
|
debug_assert_eq!(cur_lines.len() + new_lines.len(), final_lines.len());
|
||||||
|
|
||||||
|
drop(cur_lines);
|
||||||
|
|
||||||
|
// 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 final_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)?;
|
||||||
|
|
||||||
|
// Finally, update state.
|
||||||
|
self.lines.get().unwrap().replace(final_lines);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_line(&mut self, at: Line, text: Arc<str>) -> Result<(), IoError> {
|
pub fn insert_line(&mut self, at: Line, text: Arc<str>) -> Result<(), IoError> {
|
||||||
self.lines()?;
|
self.lines()?;
|
||||||
let path = self.path();
|
let path = self.path();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue