use alloc::string::String;
use alloc::vec::Vec;
use std::ffi::OsString;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::Path;
use crate::Glob;
use relative_path::RelativePath;
#[cfg_attr(windows, path = "windows.rs")]
#[cfg_attr(unix, path = "unix.rs")]
mod imp;
#[cfg(not(any(windows, unix)))]
compile_error!("root is only supported on cfg(windows) and cfg(unix)");
pub struct Root {
inner: imp::Root,
}
impl Root {
pub fn new<P>(path: P) -> io::Result<Self>
where
P: AsRef<Path>,
{
Ok(Self {
inner: imp::Root::new(path.as_ref())?,
})
}
pub fn open_options(&self) -> OpenOptions {
OpenOptions {
root: &self.inner,
options: imp::OpenOptions::new(),
}
}
pub fn create<P>(&self, path: P) -> io::Result<File>
where
P: AsRef<RelativePath>,
{
self.open_options().write(true).create(true).open(path)
}
pub fn open<P>(&self, path: P) -> io::Result<File>
where
P: AsRef<RelativePath>,
{
self.open_options().read(true).open(path)
}
pub fn read<P>(&self, path: P) -> io::Result<Vec<u8>>
where
P: AsRef<RelativePath>,
{
fn inner(this: &Root, path: &RelativePath) -> io::Result<Vec<u8>> {
let mut file = this.open(path)?;
let size = file
.metadata()
.map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX))
.ok();
let mut bytes = Vec::with_capacity(size.unwrap_or(0));
file.read_to_end(&mut bytes)?;
Ok(bytes)
}
inner(self, path.as_ref())
}
pub fn read_to_string<P>(&self, path: P) -> io::Result<String>
where
P: AsRef<RelativePath>,
{
fn inner(this: &Root, path: &RelativePath) -> io::Result<String> {
let mut file = this.open(path)?;
let size = file
.metadata()
.map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX))
.ok();
let mut string = String::with_capacity(size.unwrap_or(0));
file.read_to_string(&mut string)?;
Ok(string)
}
inner(self, path.as_ref())
}
pub fn write<P, C>(&self, path: P, contents: C) -> io::Result<()>
where
P: AsRef<RelativePath>,
C: AsRef<[u8]>,
{
self.create(path)?.write_all(contents.as_ref())
}
pub fn metadata<P>(&self, path: P) -> io::Result<Metadata>
where
P: AsRef<RelativePath>,
{
Ok(Metadata {
inner: self.inner.metadata(path.as_ref())?,
})
}
pub fn is_dir<P>(&self, path: P) -> bool
where
P: AsRef<RelativePath>,
{
self.metadata(path).map(|m| m.is_dir()).unwrap_or(false)
}
pub fn read_dir<P>(&self, path: P) -> io::Result<ReadDir>
where
P: AsRef<RelativePath>,
{
self.inner
.read_dir(path.as_ref())
.map(|inner| ReadDir { inner })
}
pub fn glob<'a, P>(&'a self, path: &'a P) -> Glob<'a>
where
P: ?Sized + AsRef<RelativePath>,
{
Glob::new(self, path.as_ref())
}
}
#[derive(Clone, Debug)]
#[must_use]
pub struct OpenOptions<'a> {
root: &'a imp::Root,
options: imp::OpenOptions,
}
impl OpenOptions<'_> {
pub fn read(&mut self, read: bool) -> &mut Self {
self.options.read(read);
self
}
pub fn write(&mut self, write: bool) -> &mut Self {
self.options.write(write);
self
}
pub fn append(&mut self, append: bool) -> &mut Self {
self.options.append(append);
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut Self {
self.options.truncate(truncate);
self
}
pub fn create(&mut self, create: bool) -> &mut Self {
self.options.create(create);
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut Self {
self.options.create_new(create_new);
self
}
pub fn open<P>(&self, path: P) -> io::Result<File>
where
P: AsRef<RelativePath>,
{
self.root.open_at(path.as_ref(), &self.options)
}
}
pub struct ReadDir {
inner: imp::ReadDir,
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let inner = self.inner.next()?;
Some(inner.map(|inner| DirEntry { inner }))
}
}
pub struct DirEntry {
inner: imp::DirEntry,
}
impl DirEntry {
#[must_use]
pub fn file_name(&self) -> OsString {
self.inner.file_name()
}
}
#[derive(Clone)]
pub struct Metadata {
inner: imp::Metadata,
}
impl Metadata {
#[must_use]
#[inline]
pub fn is_dir(&self) -> bool {
self.inner.is_dir()
}
#[must_use]
#[inline]
pub fn is_file(&self) -> bool {
self.inner.is_file()
}
#[must_use]
pub fn is_symlink(&self) -> bool {
self.inner.is_symlink()
}
}