mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-22 17:11:43 +03:00
Merge pull request #3113 from gitbutlerapp/fixup-path-handling
fixup many instances of poor path handling
This commit is contained in:
commit
a8b294d597
@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{gb_repository, writer};
|
use crate::{gb_repository, writer};
|
||||||
@ -23,7 +25,7 @@ impl<'writer> DeltasWriter<'writer> {
|
|||||||
let raw_deltas = serde_json::to_string(&deltas)?;
|
let raw_deltas = serde_json::to_string(&deltas)?;
|
||||||
|
|
||||||
self.writer
|
self.writer
|
||||||
.write_string(&format!("session/deltas/{}", path.display()), &raw_deltas)?;
|
.write_string(PathBuf::from("session/deltas").join(path), &raw_deltas)?;
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
project_id = %self.repository.get_project_id(),
|
project_id = %self.repository.get_project_id(),
|
||||||
@ -40,8 +42,7 @@ impl<'writer> DeltasWriter<'writer> {
|
|||||||
let _lock = self.repository.lock();
|
let _lock = self.repository.lock();
|
||||||
|
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
self.writer
|
self.writer.remove(PathBuf::from("session/wd").join(path))?;
|
||||||
.remove(format!("session/wd/{}", path.display()))?;
|
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
project_id = %self.repository.get_project_id(),
|
project_id = %self.repository.get_project_id(),
|
||||||
@ -59,7 +60,7 @@ impl<'writer> DeltasWriter<'writer> {
|
|||||||
|
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
self.writer
|
self.writer
|
||||||
.write_string(&format!("session/wd/{}", path.display()), contents)?;
|
.write_string(PathBuf::from("session/wd").join(path), contents)?;
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
project_id = %self.repository.get_project_id(),
|
project_id = %self.repository.get_project_id(),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::{Oid, Repository, Result};
|
use super::{Oid, Repository, Result};
|
||||||
|
use crate::path::Normalize;
|
||||||
|
|
||||||
pub struct Tree<'repo> {
|
pub struct Tree<'repo> {
|
||||||
tree: git2::Tree<'repo>,
|
tree: git2::Tree<'repo>,
|
||||||
@ -23,8 +24,11 @@ impl<'repo> Tree<'repo> {
|
|||||||
self.tree.id().into()
|
self.tree.id().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_path(&self, path: &path::Path) -> Result<TreeEntry<'repo>> {
|
pub fn get_path<P: AsRef<Path>>(&self, path: P) -> Result<TreeEntry<'repo>> {
|
||||||
self.tree.get_path(path).map(Into::into).map_err(Into::into)
|
self.tree
|
||||||
|
.get_path(path.normalize().as_path())
|
||||||
|
.map(Into::into)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk<C>(&self, mut callback: C) -> Result<()>
|
pub fn walk<C>(&self, mut callback: C) -> Result<()>
|
||||||
@ -119,12 +123,12 @@ impl<'repo> TreeBuilder<'repo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upsert<P: AsRef<path::Path>>(&mut self, filename: P, oid: Oid, filemode: FileMode) {
|
pub fn upsert<P: AsRef<Path>>(&mut self, filename: P, oid: Oid, filemode: FileMode) {
|
||||||
self.builder
|
self.builder
|
||||||
.upsert(filename.as_ref(), oid.into(), filemode.into());
|
.upsert(filename.as_ref(), oid.into(), filemode.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove<P: AsRef<path::Path>>(&mut self, filename: P) {
|
pub fn remove<P: AsRef<Path>>(&mut self, filename: P) {
|
||||||
self.builder.remove(filename.as_ref());
|
self.builder.remove(filename.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ pub(crate) mod keys;
|
|||||||
pub(crate) mod lock;
|
pub(crate) mod lock;
|
||||||
pub(crate) mod logs;
|
pub(crate) mod logs;
|
||||||
pub(crate) mod menu;
|
pub(crate) mod menu;
|
||||||
|
pub(crate) mod path;
|
||||||
pub(crate) mod project_repository;
|
pub(crate) mod project_repository;
|
||||||
pub(crate) mod projects;
|
pub(crate) mod projects;
|
||||||
pub(crate) mod reader;
|
pub(crate) mod reader;
|
||||||
|
48
gitbutler-app/src/path.rs
Normal file
48
gitbutler-app/src/path.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
|
/// Normalize a path to remove any `.` and `..` components
|
||||||
|
/// and standardize the path separator to the system's default.
|
||||||
|
///
|
||||||
|
/// This trait is automatically implemented for anything convertible
|
||||||
|
/// to a `&Path` (via `AsRef<Path>`).
|
||||||
|
pub trait Normalize {
|
||||||
|
/// Normalize a path to remove any `.` and `..` components
|
||||||
|
/// and standardize the path separator to the system's default.
|
||||||
|
fn normalize(&self) -> PathBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: AsRef<Path>> Normalize for P {
|
||||||
|
fn normalize(&self) -> PathBuf {
|
||||||
|
// Note: Copied from Cargo's codebase:
|
||||||
|
// https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
|
||||||
|
// License: MIT OR Apache-2.0 (this function only)
|
||||||
|
//
|
||||||
|
// Small modifications made by GitButler.
|
||||||
|
|
||||||
|
let path = self.as_ref();
|
||||||
|
let mut components = path.components().peekable();
|
||||||
|
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() {
|
||||||
|
components.next();
|
||||||
|
PathBuf::from(c.as_os_str())
|
||||||
|
} else {
|
||||||
|
PathBuf::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
for component in components {
|
||||||
|
match component {
|
||||||
|
Component::Prefix(..) => unreachable!(),
|
||||||
|
Component::RootDir => {
|
||||||
|
ret.push(component.as_os_str());
|
||||||
|
}
|
||||||
|
Component::CurDir => {}
|
||||||
|
Component::ParentDir => {
|
||||||
|
ret.pop();
|
||||||
|
}
|
||||||
|
Component::Normal(c) => {
|
||||||
|
ret.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,28 @@
|
|||||||
use std::{num, path, str};
|
use std::{
|
||||||
|
fs, io, num,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
str,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use serde::{ser::SerializeStruct, Serialize};
|
use serde::{ser::SerializeStruct, Serialize};
|
||||||
|
|
||||||
use crate::{fs, git, lock};
|
use crate::{git, lock, path::Normalize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, thiserror::Error)]
|
#[derive(Debug, Clone, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("file not found")]
|
#[error("file not found")]
|
||||||
NotFound,
|
NotFound,
|
||||||
#[error("io error: {0}")]
|
#[error("io error: {0}")]
|
||||||
Io(std::sync::Arc<std::io::Error>),
|
Io(Arc<io::Error>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
From(FromError),
|
From(FromError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
impl From<io::Error> for Error {
|
||||||
fn from(error: std::io::Error) -> Self {
|
fn from(error: io::Error) -> Self {
|
||||||
Error::Io(std::sync::Arc::new(error))
|
Error::Io(Arc::new(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,11 +39,11 @@ pub enum Reader<'reader> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'reader> Reader<'reader> {
|
impl<'reader> Reader<'reader> {
|
||||||
pub fn open<P: AsRef<path::Path>>(root: P) -> Result<Self, std::io::Error> {
|
pub fn open<P: AsRef<Path>>(root: P) -> Result<Self, io::Error> {
|
||||||
FilesystemReader::open(root).map(Reader::Filesystem)
|
FilesystemReader::open(root).map(Reader::Filesystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub<P: AsRef<path::Path>>(&'reader self, prefix: P) -> Self {
|
pub fn sub<P: AsRef<Path>>(&'reader self, prefix: P) -> Self {
|
||||||
Reader::Prefixed(PrefixedReader::new(self, prefix))
|
Reader::Prefixed(PrefixedReader::new(self, prefix))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +62,7 @@ impl<'reader> Reader<'reader> {
|
|||||||
Ok(Reader::Commit(CommitReader::new(repository, commit)?))
|
Ok(Reader::Commit(CommitReader::new(repository, commit)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exists<P: AsRef<path::Path>>(&self, file_path: P) -> Result<bool, std::io::Error> {
|
pub fn exists<P: AsRef<Path>>(&self, file_path: P) -> Result<bool, io::Error> {
|
||||||
match self {
|
match self {
|
||||||
Reader::Filesystem(reader) => reader.exists(file_path),
|
Reader::Filesystem(reader) => reader.exists(file_path),
|
||||||
Reader::Commit(reader) => Ok(reader.exists(file_path)),
|
Reader::Commit(reader) => Ok(reader.exists(file_path)),
|
||||||
@ -65,17 +70,17 @@ impl<'reader> Reader<'reader> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<P: AsRef<path::Path>>(&self, path: P) -> Result<Content, Error> {
|
pub fn read<P: AsRef<Path>>(&self, path: P) -> Result<Content, Error> {
|
||||||
let mut contents = self.batch(&[path])?;
|
let mut contents = self.batch(&[path])?;
|
||||||
contents
|
contents
|
||||||
.pop()
|
.pop()
|
||||||
.expect("batch should return at least one result")
|
.expect("batch should return at least one result")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn batch<P: AsRef<path::Path>>(
|
pub fn batch<P: AsRef<Path>>(
|
||||||
&self,
|
&self,
|
||||||
paths: &[P],
|
paths: &[P],
|
||||||
) -> Result<Vec<Result<Content, Error>>, std::io::Error> {
|
) -> Result<Vec<Result<Content, Error>>, io::Error> {
|
||||||
match self {
|
match self {
|
||||||
Reader::Filesystem(reader) => reader.batch(|root| {
|
Reader::Filesystem(reader) => reader.batch(|root| {
|
||||||
paths
|
paths
|
||||||
@ -85,20 +90,20 @@ impl<'reader> Reader<'reader> {
|
|||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
}
|
}
|
||||||
let content = Content::try_from(&path)?;
|
let content = Content::read_from_file(&path)?;
|
||||||
Ok(content)
|
Ok(content)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}),
|
}),
|
||||||
Reader::Commit(reader) => Ok(paths
|
Reader::Commit(reader) => Ok(paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|path| reader.read(path.as_ref()))
|
.map(|path| reader.read(path.normalize()))
|
||||||
.collect()),
|
.collect()),
|
||||||
Reader::Prefixed(reader) => reader.batch(paths),
|
Reader::Prefixed(reader) => reader.batch(paths),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_files<P: AsRef<std::path::Path>>(&self, dir_path: P) -> Result<Vec<path::PathBuf>> {
|
pub fn list_files<P: AsRef<Path>>(&self, dir_path: P) -> Result<Vec<PathBuf>> {
|
||||||
match self {
|
match self {
|
||||||
Reader::Filesystem(reader) => reader.list_files(dir_path.as_ref()),
|
Reader::Filesystem(reader) => reader.list_files(dir_path.as_ref()),
|
||||||
Reader::Commit(reader) => reader.list_files(dir_path.as_ref()),
|
Reader::Commit(reader) => reader.list_files(dir_path.as_ref()),
|
||||||
@ -110,23 +115,23 @@ impl<'reader> Reader<'reader> {
|
|||||||
pub struct FilesystemReader(lock::Dir);
|
pub struct FilesystemReader(lock::Dir);
|
||||||
|
|
||||||
impl FilesystemReader {
|
impl FilesystemReader {
|
||||||
fn open<P: AsRef<std::path::Path>>(root: P) -> Result<Self, std::io::Error> {
|
fn open<P: AsRef<Path>>(root: P) -> Result<Self, io::Error> {
|
||||||
lock::Dir::new(root).map(Self)
|
lock::Dir::new(root).map(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists<P: AsRef<std::path::Path>>(&self, path: P) -> Result<bool, std::io::Error> {
|
fn exists<P: AsRef<Path>>(&self, path: P) -> Result<bool, io::Error> {
|
||||||
let exists = self.0.batch(|root| root.join(path.as_ref()).exists())?;
|
let exists = self.0.batch(|root| root.join(path.as_ref()).exists())?;
|
||||||
Ok(exists)
|
Ok(exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn batch<R>(&self, action: impl FnOnce(&std::path::Path) -> R) -> Result<R, std::io::Error> {
|
fn batch<R>(&self, action: impl FnOnce(&Path) -> R) -> Result<R, io::Error> {
|
||||||
self.0.batch(action)
|
self.0.batch(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_files<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Vec<path::PathBuf>> {
|
fn list_files<P: AsRef<Path>>(&self, path: P) -> Result<Vec<PathBuf>> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
self.0
|
self.0
|
||||||
.batch(|root| fs::list_files(root.join(path).as_path(), &[path::Path::new(".git")]))?
|
.batch(|root| crate::fs::list_files(root.join(path).as_path(), &[Path::new(".git")]))?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,11 +160,11 @@ impl<'reader> CommitReader<'reader> {
|
|||||||
self.commit_oid
|
self.commit_oid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Content, Error> {
|
fn read<P: AsRef<Path>>(&self, path: P) -> Result<Content, Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let entry = match self
|
let entry = match self
|
||||||
.tree
|
.tree
|
||||||
.get_path(std::path::Path::new(path))
|
.get_path(Path::new(path))
|
||||||
.context(format!("{}: tree entry not found", path.display()))
|
.context(format!("{}: tree entry not found", path.display()))
|
||||||
{
|
{
|
||||||
Ok(entry) => entry,
|
Ok(entry) => entry,
|
||||||
@ -172,7 +177,7 @@ impl<'reader> CommitReader<'reader> {
|
|||||||
Ok(Content::from(&blob))
|
Ok(Content::from(&blob))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_files<P: AsRef<std::path::Path>>(&self, dir_path: P) -> Result<Vec<path::PathBuf>> {
|
fn list_files<P: AsRef<Path>>(&self, dir_path: P) -> Result<Vec<PathBuf>> {
|
||||||
let dir_path = dir_path.as_ref();
|
let dir_path = dir_path.as_ref();
|
||||||
let mut files = vec![];
|
let mut files = vec![];
|
||||||
self.tree
|
self.tree
|
||||||
@ -184,7 +189,7 @@ impl<'reader> CommitReader<'reader> {
|
|||||||
if entry.name().is_none() {
|
if entry.name().is_none() {
|
||||||
return git::TreeWalkResult::Continue;
|
return git::TreeWalkResult::Continue;
|
||||||
}
|
}
|
||||||
let entry_path = std::path::Path::new(root).join(entry.name().unwrap());
|
let entry_path = Path::new(root).join(entry.name().unwrap());
|
||||||
|
|
||||||
if !entry_path.starts_with(dir_path) {
|
if !entry_path.starts_with(dir_path) {
|
||||||
return git::TreeWalkResult::Continue;
|
return git::TreeWalkResult::Continue;
|
||||||
@ -199,28 +204,28 @@ impl<'reader> CommitReader<'reader> {
|
|||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists<P: AsRef<std::path::Path>>(&self, file_path: P) -> bool {
|
fn exists<P: AsRef<Path>>(&self, file_path: P) -> bool {
|
||||||
self.tree.get_path(file_path.as_ref()).is_ok()
|
self.tree.get_path(file_path.normalize()).is_ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PrefixedReader<'r> {
|
pub struct PrefixedReader<'r> {
|
||||||
reader: &'r Reader<'r>,
|
reader: &'r Reader<'r>,
|
||||||
prefix: path::PathBuf,
|
prefix: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> PrefixedReader<'r> {
|
impl<'r> PrefixedReader<'r> {
|
||||||
fn new<P: AsRef<path::Path>>(reader: &'r Reader, prefix: P) -> Self {
|
fn new<P: AsRef<Path>>(reader: &'r Reader, prefix: P) -> Self {
|
||||||
PrefixedReader {
|
PrefixedReader {
|
||||||
reader,
|
reader,
|
||||||
prefix: prefix.as_ref().to_path_buf(),
|
prefix: prefix.as_ref().to_path_buf(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn batch<P: AsRef<path::Path>>(
|
pub fn batch<P: AsRef<Path>>(
|
||||||
&self,
|
&self,
|
||||||
paths: &[P],
|
paths: &[P],
|
||||||
) -> Result<Vec<Result<Content, Error>>, std::io::Error> {
|
) -> Result<Vec<Result<Content, Error>>, io::Error> {
|
||||||
let paths = paths
|
let paths = paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|path| self.prefix.join(path))
|
.map(|path| self.prefix.join(path))
|
||||||
@ -228,11 +233,11 @@ impl<'r> PrefixedReader<'r> {
|
|||||||
self.reader.batch(paths.as_slice())
|
self.reader.batch(paths.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_files<P: AsRef<std::path::Path>>(&self, dir_path: P) -> Result<Vec<path::PathBuf>> {
|
fn list_files<P: AsRef<Path>>(&self, dir_path: P) -> Result<Vec<PathBuf>> {
|
||||||
self.reader.list_files(self.prefix.join(dir_path.as_ref()))
|
self.reader.list_files(self.prefix.join(dir_path.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists<P: AsRef<std::path::Path>>(&self, file_path: P) -> Result<bool, std::io::Error> {
|
fn exists<P: AsRef<Path>>(&self, file_path: P) -> Result<bool, io::Error> {
|
||||||
self.reader.exists(self.prefix.join(file_path.as_ref()))
|
self.reader.exists(self.prefix.join(file_path.as_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,6 +289,16 @@ impl Serialize for Content {
|
|||||||
|
|
||||||
impl Content {
|
impl Content {
|
||||||
const MAX_SIZE: usize = 1024 * 1024 * 10; // 10 MB
|
const MAX_SIZE: usize = 1024 * 1024 * 10; // 10 MB
|
||||||
|
|
||||||
|
pub fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let metadata = fs::metadata(path)?;
|
||||||
|
if metadata.len() > Content::MAX_SIZE as u64 {
|
||||||
|
return Ok(Content::Large);
|
||||||
|
}
|
||||||
|
let content = fs::read(path)?;
|
||||||
|
Ok(content.as_slice().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Content {
|
impl From<&str> for Content {
|
||||||
@ -296,19 +311,6 @@ impl From<&str> for Content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&path::PathBuf> for Content {
|
|
||||||
type Error = std::io::Error;
|
|
||||||
|
|
||||||
fn try_from(value: &path::PathBuf) -> Result<Self, Self::Error> {
|
|
||||||
let metadata = std::fs::metadata(value)?;
|
|
||||||
if metadata.len() > Content::MAX_SIZE as u64 {
|
|
||||||
return Ok(Content::Large);
|
|
||||||
}
|
|
||||||
let content = std::fs::read(value)?;
|
|
||||||
Ok(content.as_slice().into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&git::Blob<'_>> for Content {
|
impl From<&git::Blob<'_>> for Content {
|
||||||
fn from(value: &git::Blob) -> Self {
|
fn from(value: &git::Blob) -> Self {
|
||||||
if value.size() > Content::MAX_SIZE {
|
if value.size() > Content::MAX_SIZE {
|
||||||
@ -452,8 +454,8 @@ mod tests {
|
|||||||
fn test_directory_reader_read_file() -> Result<()> {
|
fn test_directory_reader_read_file() -> Result<()> {
|
||||||
let dir = tests::temp_dir();
|
let dir = tests::temp_dir();
|
||||||
|
|
||||||
let file_path = path::Path::new("test.txt");
|
let file_path = Path::new("test.txt");
|
||||||
std::fs::write(dir.join(file_path), "test")?;
|
fs::write(dir.join(file_path), "test")?;
|
||||||
|
|
||||||
let reader = Reader::open(dir.clone())?;
|
let reader = Reader::open(dir.clone())?;
|
||||||
assert_eq!(reader.read(file_path)?, Content::UTF8("test".to_string()));
|
assert_eq!(reader.read(file_path)?, Content::UTF8("test".to_string()));
|
||||||
@ -465,12 +467,12 @@ mod tests {
|
|||||||
fn test_commit_reader_read_file() -> Result<()> {
|
fn test_commit_reader_read_file() -> Result<()> {
|
||||||
let repository = tests::test_repository();
|
let repository = tests::test_repository();
|
||||||
|
|
||||||
let file_path = path::Path::new("test.txt");
|
let file_path = Path::new("test.txt");
|
||||||
std::fs::write(repository.path().parent().unwrap().join(file_path), "test")?;
|
fs::write(repository.path().parent().unwrap().join(file_path), "test")?;
|
||||||
|
|
||||||
let oid = tests::commit_all(&repository);
|
let oid = tests::commit_all(&repository);
|
||||||
|
|
||||||
std::fs::write(repository.path().parent().unwrap().join(file_path), "test2")?;
|
fs::write(repository.path().parent().unwrap().join(file_path), "test2")?;
|
||||||
|
|
||||||
let reader = Reader::from_commit(&repository, &repository.find_commit(oid)?)?;
|
let reader = Reader::from_commit(&repository, &repository.find_commit(oid)?)?;
|
||||||
assert_eq!(reader.read(file_path)?, Content::UTF8("test".to_string()));
|
assert_eq!(reader.read(file_path)?, Content::UTF8("test".to_string()));
|
||||||
@ -482,14 +484,14 @@ mod tests {
|
|||||||
fn test_reader_list_files_should_return_relative() -> Result<()> {
|
fn test_reader_list_files_should_return_relative() -> Result<()> {
|
||||||
let dir = tests::temp_dir();
|
let dir = tests::temp_dir();
|
||||||
|
|
||||||
std::fs::write(dir.join("test1.txt"), "test")?;
|
fs::write(dir.join("test1.txt"), "test")?;
|
||||||
std::fs::create_dir_all(dir.join("dir"))?;
|
fs::create_dir_all(dir.join("dir"))?;
|
||||||
std::fs::write(dir.join("dir").join("test.txt"), "test")?;
|
fs::write(dir.join("dir").join("test.txt"), "test")?;
|
||||||
|
|
||||||
let reader = Reader::open(dir.clone())?;
|
let reader = Reader::open(dir.clone())?;
|
||||||
let files = reader.list_files(path::Path::new("dir"))?;
|
let files = reader.list_files(Path::new("dir"))?;
|
||||||
assert_eq!(files.len(), 1);
|
assert_eq!(files.len(), 1);
|
||||||
assert!(files.contains(&path::Path::new("test.txt").to_path_buf()));
|
assert!(files.contains(&Path::new("test.txt").to_path_buf()));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -498,15 +500,15 @@ mod tests {
|
|||||||
fn test_reader_list_files() -> Result<()> {
|
fn test_reader_list_files() -> Result<()> {
|
||||||
let dir = tests::temp_dir();
|
let dir = tests::temp_dir();
|
||||||
|
|
||||||
std::fs::write(dir.join("test.txt"), "test")?;
|
fs::write(dir.join("test.txt"), "test")?;
|
||||||
std::fs::create_dir_all(dir.join("dir"))?;
|
fs::create_dir_all(dir.join("dir"))?;
|
||||||
std::fs::write(dir.join("dir").join("test.txt"), "test")?;
|
fs::write(dir.join("dir").join("test.txt"), "test")?;
|
||||||
|
|
||||||
let reader = Reader::open(dir.clone())?;
|
let reader = Reader::open(dir.clone())?;
|
||||||
let files = reader.list_files(path::Path::new(""))?;
|
let files = reader.list_files(Path::new(""))?;
|
||||||
assert_eq!(files.len(), 2);
|
assert_eq!(files.len(), 2);
|
||||||
assert!(files.contains(&path::Path::new("test.txt").to_path_buf()));
|
assert!(files.contains(&Path::new("test.txt").to_path_buf()));
|
||||||
assert!(files.contains(&path::Path::new("dir/test.txt").to_path_buf()));
|
assert!(files.contains(&Path::new("dir/test.txt").to_path_buf()));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -515,12 +517,12 @@ mod tests {
|
|||||||
fn test_commit_reader_list_files_should_return_relative() -> Result<()> {
|
fn test_commit_reader_list_files_should_return_relative() -> Result<()> {
|
||||||
let repository = tests::test_repository();
|
let repository = tests::test_repository();
|
||||||
|
|
||||||
std::fs::write(
|
fs::write(
|
||||||
repository.path().parent().unwrap().join("test1.txt"),
|
repository.path().parent().unwrap().join("test1.txt"),
|
||||||
"test",
|
"test",
|
||||||
)?;
|
)?;
|
||||||
std::fs::create_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
fs::create_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
||||||
std::fs::write(
|
fs::write(
|
||||||
repository
|
repository
|
||||||
.path()
|
.path()
|
||||||
.parent()
|
.parent()
|
||||||
@ -532,12 +534,12 @@ mod tests {
|
|||||||
|
|
||||||
let oid = tests::commit_all(&repository);
|
let oid = tests::commit_all(&repository);
|
||||||
|
|
||||||
std::fs::remove_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
fs::remove_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
||||||
|
|
||||||
let reader = CommitReader::new(&repository, &repository.find_commit(oid)?)?;
|
let reader = CommitReader::new(&repository, &repository.find_commit(oid)?)?;
|
||||||
let files = reader.list_files(path::Path::new("dir"))?;
|
let files = reader.list_files(Path::new("dir"))?;
|
||||||
assert_eq!(files.len(), 1);
|
assert_eq!(files.len(), 1);
|
||||||
assert!(files.contains(&path::Path::new("test.txt").to_path_buf()));
|
assert!(files.contains(&Path::new("test.txt").to_path_buf()));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -546,9 +548,9 @@ mod tests {
|
|||||||
fn test_commit_reader_list_files() -> Result<()> {
|
fn test_commit_reader_list_files() -> Result<()> {
|
||||||
let repository = tests::test_repository();
|
let repository = tests::test_repository();
|
||||||
|
|
||||||
std::fs::write(repository.path().parent().unwrap().join("test.txt"), "test")?;
|
fs::write(repository.path().parent().unwrap().join("test.txt"), "test")?;
|
||||||
std::fs::create_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
fs::create_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
||||||
std::fs::write(
|
fs::write(
|
||||||
repository
|
repository
|
||||||
.path()
|
.path()
|
||||||
.parent()
|
.parent()
|
||||||
@ -560,13 +562,13 @@ mod tests {
|
|||||||
|
|
||||||
let oid = tests::commit_all(&repository);
|
let oid = tests::commit_all(&repository);
|
||||||
|
|
||||||
std::fs::remove_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
fs::remove_dir_all(repository.path().parent().unwrap().join("dir"))?;
|
||||||
|
|
||||||
let reader = CommitReader::new(&repository, &repository.find_commit(oid)?)?;
|
let reader = CommitReader::new(&repository, &repository.find_commit(oid)?)?;
|
||||||
let files = reader.list_files(path::Path::new(""))?;
|
let files = reader.list_files(Path::new(""))?;
|
||||||
assert_eq!(files.len(), 2);
|
assert_eq!(files.len(), 2);
|
||||||
assert!(files.contains(&path::Path::new("test.txt").to_path_buf()));
|
assert!(files.contains(&Path::new("test.txt").to_path_buf()));
|
||||||
assert!(files.contains(&path::Path::new("dir/test.txt").to_path_buf()));
|
assert!(files.contains(&Path::new("dir/test.txt").to_path_buf()));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -575,11 +577,11 @@ mod tests {
|
|||||||
fn test_directory_reader_exists() -> Result<()> {
|
fn test_directory_reader_exists() -> Result<()> {
|
||||||
let dir = tests::temp_dir();
|
let dir = tests::temp_dir();
|
||||||
|
|
||||||
std::fs::write(dir.join("test.txt"), "test")?;
|
fs::write(dir.join("test.txt"), "test")?;
|
||||||
|
|
||||||
let reader = Reader::open(dir.clone())?;
|
let reader = Reader::open(dir.clone())?;
|
||||||
assert!(reader.exists(path::Path::new("test.txt"))?);
|
assert!(reader.exists(Path::new("test.txt"))?);
|
||||||
assert!(!reader.exists(path::Path::new("test2.txt"))?);
|
assert!(!reader.exists(Path::new("test2.txt"))?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -588,15 +590,15 @@ mod tests {
|
|||||||
fn test_commit_reader_exists() -> Result<()> {
|
fn test_commit_reader_exists() -> Result<()> {
|
||||||
let repository = tests::test_repository();
|
let repository = tests::test_repository();
|
||||||
|
|
||||||
std::fs::write(repository.path().parent().unwrap().join("test.txt"), "test")?;
|
fs::write(repository.path().parent().unwrap().join("test.txt"), "test")?;
|
||||||
|
|
||||||
let oid = tests::commit_all(&repository);
|
let oid = tests::commit_all(&repository);
|
||||||
|
|
||||||
std::fs::remove_file(repository.path().parent().unwrap().join("test.txt"))?;
|
fs::remove_file(repository.path().parent().unwrap().join("test.txt"))?;
|
||||||
|
|
||||||
let reader = CommitReader::new(&repository, &repository.find_commit(oid)?)?;
|
let reader = CommitReader::new(&repository, &repository.find_commit(oid)?)?;
|
||||||
assert!(reader.exists(path::Path::new("test.txt")));
|
assert!(reader.exists(Path::new("test.txt")));
|
||||||
assert!(!reader.exists(path::Path::new("test2.txt")));
|
assert!(!reader.exists(Path::new("test2.txt")));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ impl Handler {
|
|||||||
if !full_path.exists() {
|
if !full_path.exists() {
|
||||||
return Err(reader::Error::NotFound);
|
return Err(reader::Error::NotFound);
|
||||||
}
|
}
|
||||||
reader::Content::try_from(&full_path).map_err(Into::into)
|
Ok(reader::Content::read_from_file(&full_path)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle<P: AsRef<std::path::Path>>(
|
pub fn handle<P: AsRef<std::path::Path>>(
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::lock;
|
use crate::lock;
|
||||||
@ -5,7 +7,7 @@ use crate::lock;
|
|||||||
pub struct DirWriter(lock::Dir);
|
pub struct DirWriter(lock::Dir);
|
||||||
|
|
||||||
impl DirWriter {
|
impl DirWriter {
|
||||||
pub fn open<P: AsRef<std::path::Path>>(root: P) -> Result<Self, std::io::Error> {
|
pub fn open<P: AsRef<Path>>(root: P) -> Result<Self, std::io::Error> {
|
||||||
lock::Dir::new(root).map(Self)
|
lock::Dir::new(root).map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13,13 +15,13 @@ impl DirWriter {
|
|||||||
impl DirWriter {
|
impl DirWriter {
|
||||||
fn write<P, C>(&self, path: P, contents: C) -> Result<(), std::io::Error>
|
fn write<P, C>(&self, path: P, contents: C) -> Result<(), std::io::Error>
|
||||||
where
|
where
|
||||||
P: AsRef<std::path::Path>,
|
P: AsRef<Path>,
|
||||||
C: AsRef<[u8]>,
|
C: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
self.batch(&[BatchTask::Write(path, contents)])
|
self.batch(&[BatchTask::Write(path, contents)])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), std::io::Error> {
|
pub fn remove<P: AsRef<Path>>(&self, path: P) -> Result<(), std::io::Error> {
|
||||||
self.0.batch(|root| {
|
self.0.batch(|root| {
|
||||||
let path = root.join(path);
|
let path = root.join(path);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
@ -36,7 +38,7 @@ impl DirWriter {
|
|||||||
|
|
||||||
pub fn batch<P, C>(&self, values: &[BatchTask<P, C>]) -> Result<(), std::io::Error>
|
pub fn batch<P, C>(&self, values: &[BatchTask<P, C>]) -> Result<(), std::io::Error>
|
||||||
where
|
where
|
||||||
P: AsRef<std::path::Path>,
|
P: AsRef<Path>,
|
||||||
C: AsRef<[u8]>,
|
C: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
self.0.batch(|root| {
|
self.0.batch(|root| {
|
||||||
@ -67,12 +69,16 @@ impl DirWriter {
|
|||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_string(&self, path: &str, contents: &str) -> Result<(), std::io::Error> {
|
pub fn write_string<P: AsRef<Path>>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
contents: &str,
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
self.write(path, contents)
|
self.write(path, contents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BatchTask<P: AsRef<std::path::Path>, C: AsRef<[u8]>> {
|
pub enum BatchTask<P: AsRef<Path>, C: AsRef<[u8]>> {
|
||||||
Write(P, C),
|
Write(P, C),
|
||||||
Remove(P),
|
Remove(P),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user