pub trait PathExt
{
#[cfg(unix)]
#[inline(always)]
fn to_c_string(&self) -> CString;
#[cfg(unix)]
#[inline(always)]
fn make_file_read_write_all(&self) -> io::Result<()>;
#[cfg(unix)]
#[inline(always)]
fn make_folder_searchable_to_all(&self) -> io::Result<()>;
#[inline(always)]
fn read_hexadecimal_value_with_prefix<P: Fn(&str) -> Result<T, ParseIntError>, T>(&self, size: usize, parser: P) -> io::Result<T>;
#[inline(always)]
fn read_hexadecimal_value_with_prefix_u16(&self) -> io::Result<u16>
{
self.read_hexadecimal_value_with_prefix(4, |raw_string| u16::from_str_radix(raw_string, 16))
}
#[inline(always)]
fn read_raw(&self) -> io::Result<Box<[u8]>>;
#[inline(always)]
fn read_raw_without_line_feed(&self) -> io::Result<Box<[u8]>>;
#[inline(always)]
fn read_raw_string(&self) -> io::Result<String>;
#[inline(always)]
fn read_string_without_line_feed(&self) -> io::Result<String>;
#[inline(always)]
fn read_value<F>(&self) -> io::Result<F> where F: FromStr, <F as FromStr>::Err: 'static + Send + Sync + error::Error;
#[inline(always)]
fn write_value<D: Display>(&self, value: D) -> io::Result<()>;
#[inline(always)]
fn read_linux_core_or_numa_list<Mapper: Fn(u16) -> R, R: Ord>(&self, mapper: Mapper) -> Result<BTreeSet<R>, ListParseError>;
#[inline(always)]
fn parse_linux_core_or_numa_bitmask(&self) -> Result<u32, io::Error>;
#[inline(always)]
fn parse_process_status_file(&self) -> Result<ProcessStatusStatistics, ProcessStatusFileParseError>;
#[inline(always)]
fn parse_virtual_memory_statistics_file(&self) -> io::Result<HashMap<VirtualMemoryStatisticName, u64>>;
#[inline(always)]
fn parse_memory_information_file(&self, memory_information_name_prefix: &[u8]) -> Result<MemoryInformation, MemoryInformationParseError>;
}
impl PathExt for Path
{
#[cfg(unix)]
#[inline(always)]
fn to_c_string(&self) -> CString
{
CString::new(self.as_os_str().as_bytes()).expect("Paths should not contain interior ASCII NULs")
}
#[cfg(unix)]
#[inline(always)]
fn make_file_read_write_all(&self) -> io::Result<()>
{
#[inline(always)]
fn add_read_write_permissions(permissions: Permissions) -> Permissions
{
Permissions::from_mode(permissions.mode() | 0o666)
}
let metadata = metadata(self)?;
set_permissions(self, add_read_write_permissions(metadata.permissions()))
}
#[cfg(unix)]
#[inline(always)]
fn make_folder_searchable_to_all(&self) -> io::Result<()>
{
#[inline(always)]
fn add_read_and_execute_permissions(permissions: Permissions) -> Permissions
{
Permissions::from_mode(permissions.mode() | 0o555)
}
let metadata = metadata(self)?;
set_permissions(self, add_read_and_execute_permissions(metadata.permissions()))
}
#[inline(always)]
fn read_hexadecimal_value_with_prefix<P: Fn(&str) -> Result<T, ParseIntError>, T>(&self, size: usize, parser: P) -> io::Result<T>
{
use self::ErrorKind::InvalidData;
let raw_string = self.read_string_without_line_feed()?;
let size_wih_0x_prefix = 2 + size;
if raw_string.len() != size_wih_0x_prefix
{
return Err(io::Error::new(InvalidData, format!("{} bytes not read", size_wih_0x_prefix)));
}
match &raw_string[..2]
{
"0x" => (),
_ => return Err(io::Error::new(InvalidData, "value does not start '0x'")),
}
match parser(&raw_string[2..])
{
Err(error) => Err(io::Error::new(InvalidData, error)),
Ok(value) => Ok(value),
}
}
#[inline(always)]
fn read_raw(&self) -> io::Result<Box<[u8]>>
{
let raw = ::std::fs::read(self)?.into_boxed_slice();
if raw.is_empty()
{
Err(io::Error::new(ErrorKind::InvalidData, "Empty file"))
}
else
{
Ok(raw)
}
}
#[inline(always)]
fn read_raw_without_line_feed(&self) -> io::Result<Box<[u8]>>
{
let mut raw = self.read_raw()?.to_vec();
let length = raw.len();
let should_be_line_feed = raw.remove(length - 1);
if should_be_line_feed != b'\n'
{
return Err(io::Error::new(ErrorKind::InvalidData, "File lacks terminating line feed"));
}
Ok(raw.into_boxed_slice())
}
#[inline(always)]
fn read_raw_string(&self) -> io::Result<String>
{
let raw_string = read_to_string(self)?;
if raw_string.is_empty()
{
Err(io::Error::new(ErrorKind::InvalidData, "Empty file"))
}
else
{
Ok(raw_string)
}
}
#[inline(always)]
fn read_string_without_line_feed(&self) -> io::Result<String>
{
let mut raw_string = self.read_raw_string()?;
let length = raw_string.len();
let should_be_line_feed = raw_string.remove(length - 1);
if should_be_line_feed != '\n'
{
return Err(io::Error::new(ErrorKind::InvalidData, "File lacks terminating line feed"));
}
Ok(raw_string)
}
#[inline(always)]
fn read_value<F>(&self) -> io::Result<F> where F: FromStr, <F as FromStr>::Err: 'static + Send + Sync + error::Error
{
let string = self.read_string_without_line_feed()?;
match string.parse::<F>()
{
Err(error) => Err(io::Error::new(ErrorKind::InvalidData, error)),
Ok(value) => Ok(value),
}
}
#[inline(always)]
fn write_value<D: Display>(&self, value: D) -> io::Result<()>
{
let value = format!("{}\n", value).into_bytes();
let mut file = OpenOptions::new().write(true).open(self)?;
file.write_all(value.as_slice())
}
#[inline(always)]
fn read_linux_core_or_numa_list<Mapper: Fn(u16) -> R, R: Ord>(&self, mapper: Mapper) -> Result<BTreeSet<R>, ListParseError>
{
let without_line_feed = self.read_raw_without_line_feed()?;
ListParseError::parse_linux_list_string::<Mapper, R>(&without_line_feed, mapper)
}
#[inline(always)]
fn parse_linux_core_or_numa_bitmask(&self) -> Result<u32, io::Error>
{
let without_line_feed = self.read_string_without_line_feed()?;
if without_line_feed.len() != 8
{
return Err(io::Error::new(ErrorKind::InvalidData, "Linux core or numa mask string should be 8 characters long"))
}
u32::from_str_radix(&without_line_feed, 16).map_err(|error| io::Error::new(ErrorKind::InvalidData, error))
}
#[inline(always)]
fn parse_process_status_file(&self) -> Result<ProcessStatusStatistics, ProcessStatusFileParseError>
{
let file = File::open(self)?;
let reader = BufReader::with_capacity(4096, file);
ProcessStatusStatistics::parse(reader)
}
#[inline(always)]
fn parse_virtual_memory_statistics_file(&self) -> io::Result<HashMap<VirtualMemoryStatisticName, u64>>
{
let file = File::open(self)?;
let reader = BufReader::with_capacity(4096, file);
let mut statistics = HashMap::with_capacity(6);
let mut zero_based_line_number = 0;
for line in reader.split(b'\n')
{
let mut line = line?;
{
use self::ErrorKind::InvalidData;
let mut split = splitn(&line, 2, b' ');
let statistic_name = VirtualMemoryStatisticName::parse(split.next().unwrap());
let statistic_value = match split.next()
{
None => return Err(io::Error::new(InvalidData, format!("Zero based line '{}' does not have a value second column", zero_based_line_number))),
Some(value) =>
{
let str_value = match from_utf8(value)
{
Err(utf8_error) => return Err(io::Error::new(InvalidData, utf8_error)),
Ok(str_value) => str_value,
};
match str_value.parse::<u64>()
{
Err(parse_error) => return Err(io::Error::new(InvalidData, parse_error)),
Ok(value) => value,
}
}
};
if let Some(previous) = statistics.insert(statistic_name, statistic_value)
{
return Err(io::Error::new(InvalidData, format!("Zero based line '{}' has a duplicate statistic (was '{}')", zero_based_line_number, previous)))
}
}
line.clear();
zero_based_line_number += 1;
}
Ok(statistics)
}
fn parse_memory_information_file(&self, memory_information_name_prefix: &[u8]) -> Result<MemoryInformation, MemoryInformationParseError>
{
let reader = BufReader::with_capacity(4096, File::open(self)?);
let mut map = HashMap::new();
let mut zero_based_line_number = 0;
use self::MemoryInformationParseError::*;
for line in reader.split(b'\n')
{
let mut line = line?;
{
let mut split = splitn(&line, 2, b':');
let memory_information_name = MemoryInformationName::parse(split.next().unwrap(), memory_information_name_prefix);
let memory_information_value = match split.next()
{
None => return Err(MemoryInformationParseError::CouldNotParseMemoryInformationValue { zero_based_line_number, memory_information_name }),
Some(raw_value) =>
{
let str_value = match from_utf8(raw_value)
{
Err(utf8_error) => return Err(CouldNotParseAsUtf8 { zero_based_line_number, memory_information_name, bad_value: raw_value.to_vec().into_boxed_slice(), cause: utf8_error }),
Ok(str_value) => str_value,
};
let trimmed_str_value = str_value.trim();
let ends_with = memory_information_name.unit().ends_with();
if !trimmed_str_value.ends_with(ends_with)
{
return Err(CouldNotParseMemoryInformationValueTrimmed { zero_based_line_number, memory_information_name, bad_value: trimmed_str_value.to_owned() });
}
let trimmed = &trimmed_str_value[0 .. trimmed_str_value.len() - ends_with.len()];
match trimmed.parse::<u64>()
{
Ok(value) => value,
Err(int_parse_error) => return Err(CouldNotParseMemoryInformationValueAsU64 { zero_based_line_number, memory_information_name, bad_value: trimmed.to_owned(), cause: int_parse_error })
}
}
};
if map.contains_key(&memory_information_name)
{
return Err(DuplicateMemoryInformation { zero_based_line_number, memory_information_name, new_value: memory_information_value });
}
map.insert(memory_information_name, memory_information_value);
}
line.clear();
zero_based_line_number += 1;
}
Ok(MemoryInformation(map))
}
}