use std::{
borrow::{Borrow, Cow},
collections::HashMap,
ops::DerefMut,
};
use crate::{
file::{
error::GitConfigError,
git_config::SectionId,
section::{MutableSection, SectionBody},
Index, Size,
},
parser::{Event, Key},
values::{normalize_bytes, normalize_vec},
};
#[allow(clippy::module_name_repetitions)]
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct MutableValue<'borrow, 'lookup, 'event> {
section: MutableSection<'borrow, 'event>,
key: Key<'lookup>,
index: Index,
size: Size,
}
impl<'borrow, 'lookup, 'event> MutableValue<'borrow, 'lookup, 'event> {
pub(super) const fn new(
section: MutableSection<'borrow, 'event>,
key: Key<'lookup>,
index: Index,
size: Size,
) -> Self {
Self {
section,
key,
index,
size,
}
}
#[inline]
pub fn get(&self) -> Result<Cow<'_, [u8]>, GitConfigError> {
self.section.get(&self.key, self.index, self.index + self.size)
}
#[inline]
pub fn set_string(&mut self, input: String) {
self.set_bytes(input.into_bytes());
}
pub fn set_bytes(&mut self, input: Vec<u8>) {
if self.size.0 > 0 {
self.section.delete(self.index, self.index + self.size);
}
self.size = Size(3);
self.section
.set_internal(self.index, Key(Cow::Owned(self.key.to_string())), input);
}
pub fn delete(&mut self) {
if self.size.0 > 0 {
self.section.delete(self.index, self.index + self.size);
self.size = Size(0);
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub(super) struct EntryData {
section_id: SectionId,
offset_index: usize,
}
impl EntryData {
pub(super) const fn new(section_id: SectionId, offset_index: usize) -> Self {
Self {
section_id,
offset_index,
}
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(PartialEq, Eq, Debug)]
pub struct MutableMultiValue<'borrow, 'lookup, 'event> {
section: &'borrow mut HashMap<SectionId, SectionBody<'event>>,
key: Key<'lookup>,
indices_and_sizes: Vec<EntryData>,
offsets: HashMap<SectionId, Vec<usize>>,
}
impl<'borrow, 'lookup, 'event> MutableMultiValue<'borrow, 'lookup, 'event> {
pub(super) fn new(
section: &'borrow mut HashMap<SectionId, SectionBody<'event>>,
key: Key<'lookup>,
indices_and_sizes: Vec<EntryData>,
offsets: HashMap<SectionId, Vec<usize>>,
) -> Self {
Self {
section,
key,
indices_and_sizes,
offsets,
}
}
pub fn get(&self) -> Result<Vec<Cow<'_, [u8]>>, GitConfigError> {
let mut found_key = false;
let mut values = vec![];
let mut partial_value = None;
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
let (offset, size) = MutableMultiValue::get_index_and_size(&self.offsets, *section_id, *offset_index);
for event in &self
.section
.get(section_id)
.expect("sections does not have section id from section ids")
.as_ref()[offset..offset + size]
{
match event {
Event::Key(event_key) if *event_key == self.key => found_key = true,
Event::Value(v) if found_key => {
found_key = false;
values.push(normalize_bytes(v.borrow()));
}
Event::ValueNotDone(v) if found_key => {
partial_value = Some((*v).to_vec());
}
Event::ValueDone(v) if found_key => {
found_key = false;
let mut value = partial_value
.take()
.expect("Somehow got ValueDone before ValueNotDone event");
value.extend(&**v);
values.push(normalize_vec(value));
}
_ => (),
}
}
}
if values.is_empty() {
return Err(GitConfigError::KeyDoesNotExist);
}
Ok(values)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.indices_and_sizes.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.indices_and_sizes.is_empty()
}
#[inline]
pub fn set_string(&mut self, index: usize, input: String) {
self.set_bytes(index, input.into_bytes());
}
#[inline]
pub fn set_bytes(&mut self, index: usize, input: Vec<u8>) {
self.set_value(index, Cow::Owned(input));
}
pub fn set_value<'a: 'event>(&mut self, index: usize, input: Cow<'a, [u8]>) {
let EntryData {
section_id,
offset_index,
} = self.indices_and_sizes[index];
MutableMultiValue::set_value_inner(
&self.key,
&mut self.offsets,
self.section
.get_mut(§ion_id)
.expect("sections does not have section id from section ids"),
section_id,
offset_index,
input,
);
}
#[inline]
pub fn set_values<'a: 'event>(&mut self, input: impl Iterator<Item = Cow<'a, [u8]>>) {
for (
EntryData {
section_id,
offset_index,
},
value,
) in self.indices_and_sizes.iter().zip(input)
{
Self::set_value_inner(
&self.key,
&mut self.offsets,
self.section
.get_mut(section_id)
.expect("sections does not have section id from section ids"),
*section_id,
*offset_index,
value,
);
}
}
#[inline]
pub fn set_str_all(&mut self, input: &str) {
self.set_owned_values_all(input.as_bytes());
}
#[inline]
pub fn set_owned_values_all(&mut self, input: &[u8]) {
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
Self::set_value_inner(
&self.key,
&mut self.offsets,
self.section
.get_mut(section_id)
.expect("sections does not have section id from section ids"),
*section_id,
*offset_index,
Cow::Owned(input.to_vec()),
);
}
}
#[inline]
pub fn set_values_all<'a: 'event>(&mut self, input: &'a [u8]) {
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
Self::set_value_inner(
&self.key,
&mut self.offsets,
self.section
.get_mut(section_id)
.expect("sections does not have section id from section ids"),
*section_id,
*offset_index,
Cow::Borrowed(input),
);
}
}
fn set_value_inner<'a: 'event>(
key: &Key<'lookup>,
offsets: &mut HashMap<SectionId, Vec<usize>>,
section: &mut SectionBody<'event>,
section_id: SectionId,
offset_index: usize,
input: Cow<'a, [u8]>,
) {
let (offset, size) = MutableMultiValue::get_index_and_size(offsets, section_id, offset_index);
section.as_mut().drain(offset..offset + size);
MutableMultiValue::set_offset(offsets, section_id, offset_index, 3);
section.as_mut().insert(offset, Event::Value(input));
section.as_mut().insert(offset, Event::KeyValueSeparator);
section
.as_mut()
.insert(offset, Event::Key(Key(Cow::Owned(key.0.to_string()))));
}
pub fn delete(&mut self, index: usize) {
let EntryData {
section_id,
offset_index,
} = &self.indices_and_sizes[index];
let (offset, size) = MutableMultiValue::get_index_and_size(&self.offsets, *section_id, *offset_index);
if size > 0 {
self.section
.get_mut(section_id)
.expect("sections does not have section id from section ids")
.as_mut()
.drain(offset..offset + size);
Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
self.indices_and_sizes.remove(index);
}
}
pub fn delete_all(&mut self) {
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
let (offset, size) = MutableMultiValue::get_index_and_size(&self.offsets, *section_id, *offset_index);
if size > 0 {
self.section
.get_mut(section_id)
.expect("sections does not have section id from section ids")
.as_mut()
.drain(offset..offset + size);
Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
}
}
self.indices_and_sizes.clear();
}
#[inline]
fn get_index_and_size(
offsets: &'lookup HashMap<SectionId, Vec<usize>>,
section_id: SectionId,
offset_index: usize,
) -> (usize, usize) {
offsets
.get(§ion_id)
.expect("sections does not have section id from section ids")
.iter()
.take(offset_index + 1)
.fold((0, 0), |(old, new), offset| (old + new, *offset))
}
#[inline]
fn set_offset(
offsets: &mut HashMap<SectionId, Vec<usize>>,
section_id: SectionId,
offset_index: usize,
value: usize,
) {
*offsets
.get_mut(§ion_id)
.expect("sections does not have section id from section ids")
.get_mut(offset_index)
.unwrap()
.deref_mut() = value;
}
}