Unified · Split
| @@ -10,27 +10,7 @@ pub use store::*; · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | pub struct SafeName<'a>(&'a BStr); | 11 | pub struct SafeName<'a>(&'a BStr); |
| 12 | 12 | ||
| 13 | mod safe_name { | 13 | mod safe_name; |
| 14 | use crate::SafeName; | ||
| 15 | use bstr::{BStr, ByteSlice}; | ||
| 16 | use std::convert::TryFrom; | ||
| 17 | |||
| 18 | impl<'a> TryFrom<&'a BStr> for SafeName<'a> { | ||
| 19 | type Error = git_validate::reference::name::Error; | ||
| 20 | |||
| 21 | fn try_from(v: &'a BStr) -> Result<Self, Self::Error> { | ||
| 22 | Ok(SafeName(git_validate::refname(v)?)) | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | impl<'a> TryFrom<&'a str> for SafeName<'a> { | ||
| 27 | type Error = git_validate::reference::name::Error; | ||
| 28 | |||
| 29 | fn try_from(v: &'a str) -> Result<Self, Self::Error> { | ||
| 30 | Ok(SafeName(git_validate::refname(v.as_bytes().as_bstr())?)) | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | 14 | ||
| 35 | /// Denotes the kind of function to produce a `Id` | 15 | /// Denotes the kind of function to produce a `Id` |
| 36 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] | 16 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] |
| @@ -0,0 +1,19 @@ | |||
| 1 | use crate::SafeName; | ||
| 2 | use bstr::{BStr, ByteSlice}; | ||
| 3 | use std::convert::TryFrom; | ||
| 4 | |||
| 5 | impl<'a> TryFrom<&'a BStr> for SafeName<'a> { | ||
| 6 | type Error = git_validate::reference::name::Error; | ||
| 7 | |||
| 8 | fn try_from(v: &'a BStr) -> Result<Self, Self::Error> { | ||
| 9 | Ok(SafeName(git_validate::refname(v)?)) | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<'a> TryFrom<&'a str> for SafeName<'a> { | ||
| 14 | type Error = git_validate::reference::name::Error; | ||
| 15 | |||
| 16 | fn try_from(v: &'a str) -> Result<Self, Self::Error> { | ||
| 17 | Ok(SafeName(git_validate::refname(v.as_bytes().as_bstr())?)) | ||
| 18 | } | ||
| 19 | } | ||
| @@ -1,178 +0,0 @@ | |||
| 1 | use std::path::PathBuf; | ||
| 2 | |||
| 3 | #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone)] | ||
| 4 | pub struct Reference<'a> { | ||
| 5 | parent: &'a Store, | ||
| 6 | /// The path relative to the stores base at which this reference is located | ||
| 7 | pub relative_path: PathBuf, | ||
| 8 | state: reference::State, | ||
| 9 | } | ||
| 10 | |||
| 11 | #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone)] | ||
| 12 | pub struct Store { | ||
| 13 | /// The location at which loose references can be found as per conventions of a typical git repository | ||
| 14 | pub base: PathBuf, | ||
| 15 | } | ||
| 16 | |||
| 17 | mod backend { | ||
| 18 | mod find { | ||
| 19 | use crate::{loose, SafeName}; | ||
| 20 | use quick_error::quick_error; | ||
| 21 | use std::convert::TryInto; | ||
| 22 | |||
| 23 | quick_error! { | ||
| 24 | #[derive(Debug)] | ||
| 25 | pub enum Error { | ||
| 26 | Tbd | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | impl loose::Store { | ||
| 31 | pub fn find_one<'a>(&self, _path: impl TryInto<SafeName<'a>>) -> Result<loose::Reference<'_>, Error> { | ||
| 32 | todo!("find one") | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | mod init { | ||
| 38 | use crate::loose; | ||
| 39 | use std::path::PathBuf; | ||
| 40 | |||
| 41 | impl loose::Store { | ||
| 42 | pub fn new(path: impl Into<PathBuf>) -> Self { | ||
| 43 | loose::Store { base: path.into() } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | impl<P> From<P> for loose::Store | ||
| 48 | where | ||
| 49 | P: Into<PathBuf>, | ||
| 50 | { | ||
| 51 | fn from(path: P) -> Self { | ||
| 52 | loose::Store::new(path) | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | pub mod reference { | ||
| 59 | use crate::{loose::Reference, Kind, Target}; | ||
| 60 | use bstr::BString; | ||
| 61 | use git_hash::ObjectId; | ||
| 62 | |||
| 63 | #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone)] | ||
| 64 | #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] | ||
| 65 | pub(crate) enum State { | ||
| 66 | Id(ObjectId), | ||
| 67 | Path(BString), | ||
| 68 | } | ||
| 69 | |||
| 70 | impl<'a> Reference<'a> { | ||
| 71 | pub fn kind(&self) -> Kind { | ||
| 72 | match self.state { | ||
| 73 | State::Path(_) => Kind::Symbolic, | ||
| 74 | State::Id(_) => Kind::Peeled, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | pub fn target(&'a self) -> Target<'a> { | ||
| 78 | match self.state { | ||
| 79 | State::Path(ref path) => Target::Symbolic(path.as_ref()), | ||
| 80 | State::Id(ref oid) => Target::Peeled(oid.as_ref()), | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | pub mod decode { | ||
| 86 | use crate::loose::{reference::State, Reference, Store}; | ||
| 87 | use bstr::BString; | ||
| 88 | use git_hash::ObjectId; | ||
| 89 | use nom::{ | ||
| 90 | branch::alt, | ||
| 91 | bytes::complete::take_while, | ||
| 92 | bytes::complete::{tag, take_while_m_n}, | ||
| 93 | combinator::{map, opt}, | ||
| 94 | sequence::terminated, | ||
| 95 | IResult, | ||
| 96 | }; | ||
| 97 | use quick_error::quick_error; | ||
| 98 | use std::convert::TryInto; | ||
| 99 | use std::{convert::TryFrom, path::PathBuf}; | ||
| 100 | |||
| 101 | enum MaybeUnsafeState { | ||
| 102 | Id(ObjectId), | ||
| 103 | UnvalidatedPath(BString), | ||
| 104 | } | ||
| 105 | |||
| 106 | quick_error! { | ||
| 107 | #[derive(Debug)] | ||
| 108 | pub enum Error { | ||
| 109 | Parse(content: BString) { | ||
| 110 | display("{:?} could not be parsed", content) | ||
| 111 | } | ||
| 112 | RefnameValidation{err: git_validate::reference::name::Error, path: BString} { | ||
| 113 | display("The path to a symbolic reference is invalid") | ||
| 114 | source(err) | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | impl TryFrom<MaybeUnsafeState> for State { | ||
| 120 | type Error = Error; | ||
| 121 | |||
| 122 | fn try_from(v: MaybeUnsafeState) -> Result<Self, Self::Error> { | ||
| 123 | Ok(match v { | ||
| 124 | MaybeUnsafeState::Id(id) => State::Id(id), | ||
| 125 | MaybeUnsafeState::UnvalidatedPath(path) => { | ||
| 126 | State::Path(match git_validate::refname(path.as_ref()) { | ||
| 127 | Err(err) => return Err(Error::RefnameValidation { err, path }), | ||
| 128 | Ok(_) => path, | ||
| 129 | }) | ||
| 130 | } | ||
| 131 | }) | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | impl<'a> Reference<'a> { | ||
| 136 | pub fn try_from_path( | ||
| 137 | parent: &'a Store, | ||
| 138 | relative_path: impl Into<PathBuf>, | ||
| 139 | path_contents: &[u8], | ||
| 140 | ) -> Result<Self, Error> { | ||
| 141 | Ok(Reference { | ||
| 142 | parent, | ||
| 143 | relative_path: relative_path.into(), | ||
| 144 | state: parse(path_contents) | ||
| 145 | .map_err(|err| err.to_string()) | ||
| 146 | .map_err(|_| Error::Parse(path_contents.into()))? | ||
| 147 | .1 | ||
| 148 | .try_into()?, | ||
| 149 | }) | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | fn is_hex_digit_lc(b: u8) -> bool { | ||
| 154 | matches!(b, b'0'..=b'9' | b'a'..=b'f') | ||
| 155 | } | ||
| 156 | |||
| 157 | fn hex_sha1(i: &[u8]) -> IResult<&[u8], &[u8]> { | ||
| 158 | take_while_m_n(40usize, 40, is_hex_digit_lc)(i) | ||
| 159 | } | ||
| 160 | |||
| 161 | fn newline(i: &[u8]) -> IResult<&[u8], &[u8]> { | ||
| 162 | alt((tag(b"\r\n"), tag(b"\n")))(i) | ||
| 163 | } | ||
| 164 | |||
| 165 | fn parse(bytes: &[u8]) -> IResult<&[u8], MaybeUnsafeState> { | ||
| 166 | let is_space = |b: u8| b == b' '; | ||
| 167 | if let (path, Some(_ref_prefix)) = opt(terminated(tag("ref: "), take_while(is_space)))(bytes)? { | ||
| 168 | map(terminated(take_while(|b| b != b'\r' && b != b'\n'), newline), |path| { | ||
| 169 | MaybeUnsafeState::UnvalidatedPath(path.into()) | ||
| 170 | })(path) | ||
| 171 | } else { | ||
| 172 | map(terminated(hex_sha1, newline), |hex| { | ||
| 173 | MaybeUnsafeState::Id(ObjectId::from_hex(hex).expect("prior validation")) | ||
| 174 | })(bytes) | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| @@ -0,0 +1,38 @@ | |||
| 1 | mod find { | ||
| 2 | use crate::{loose, SafeName}; | ||
| 3 | use quick_error::quick_error; | ||
| 4 | use std::convert::TryInto; | ||
| 5 | |||
| 6 | quick_error! { | ||
| 7 | #[derive(Debug)] | ||
| 8 | pub enum Error { | ||
| 9 | Tbd | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | impl loose::Store { | ||
| 14 | pub fn find_one<'a>(&self, _path: impl TryInto<SafeName<'a>>) -> Result<loose::Reference<'_>, Error> { | ||
| 15 | todo!("find one") | ||
| 16 | } | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | mod init { | ||
| 21 | use crate::loose; | ||
| 22 | use std::path::PathBuf; | ||
| 23 | |||
| 24 | impl loose::Store { | ||
| 25 | pub fn new(path: impl Into<PathBuf>) -> Self { | ||
| 26 | loose::Store { base: path.into() } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | impl<P> From<P> for loose::Store | ||
| 31 | where | ||
| 32 | P: Into<PathBuf>, | ||
| 33 | { | ||
| 34 | fn from(path: P) -> Self { | ||
| 35 | loose::Store::new(path) | ||
| 36 | } | ||
| 37 | } | ||
| 38 | } | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | use std::path::PathBuf; | ||
| 2 | |||
| 3 | #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone)] | ||
| 4 | pub struct Reference<'a> { | ||
| 5 | parent: &'a Store, | ||
| 6 | /// The path relative to the stores base at which this reference is located | ||
| 7 | pub relative_path: PathBuf, | ||
| 8 | state: reference::State, | ||
| 9 | } | ||
| 10 | |||
| 11 | #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone)] | ||
| 12 | pub struct Store { | ||
| 13 | /// The location at which loose references can be found as per conventions of a typical git repository | ||
| 14 | pub base: PathBuf, | ||
| 15 | } | ||
| 16 | |||
| 17 | mod backend; | ||
| 18 | pub mod reference; | ||
| @@ -0,0 +1,116 @@ | |||
| 1 | use crate::{loose::Reference, Kind, Target}; | ||
| 2 | use bstr::BString; | ||
| 3 | use git_hash::ObjectId; | ||
| 4 | |||
| 5 | #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone)] | ||
| 6 | #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] | ||
| 7 | pub(crate) enum State { | ||
| 8 | Id(ObjectId), | ||
| 9 | Path(BString), | ||
| 10 | } | ||
| 11 | |||
| 12 | impl<'a> Reference<'a> { | ||
| 13 | pub fn kind(&self) -> Kind { | ||
| 14 | match self.state { | ||
| 15 | State::Path(_) => Kind::Symbolic, | ||
| 16 | State::Id(_) => Kind::Peeled, | ||
| 17 | } | ||
| 18 | } | ||
| 19 | pub fn target(&'a self) -> Target<'a> { | ||
| 20 | match self.state { | ||
| 21 | State::Path(ref path) => Target::Symbolic(path.as_ref()), | ||
| 22 | State::Id(ref oid) => Target::Peeled(oid.as_ref()), | ||
| 23 | } | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | pub mod decode { | ||
| 28 | use crate::loose::{reference::State, Reference, Store}; | ||
| 29 | use bstr::BString; | ||
| 30 | use git_hash::ObjectId; | ||
| 31 | use nom::{ | ||
| 32 | branch::alt, | ||
| 33 | bytes::complete::take_while, | ||
| 34 | bytes::complete::{tag, take_while_m_n}, | ||
| 35 | combinator::{map, opt}, | ||
| 36 | sequence::terminated, | ||
| 37 | IResult, | ||
| 38 | }; | ||
| 39 | use quick_error::quick_error; | ||
| 40 | use std::convert::TryInto; | ||
| 41 | use std::{convert::TryFrom, path::PathBuf}; | ||
| 42 | |||
| 43 | enum MaybeUnsafeState { | ||
| 44 | Id(ObjectId), | ||
| 45 | UnvalidatedPath(BString), | ||
| 46 | } | ||
| 47 | |||
| 48 | quick_error! { | ||
| 49 | #[derive(Debug)] | ||
| 50 | pub enum Error { | ||
| 51 | Parse(content: BString) { | ||
| 52 | display("{:?} could not be parsed", content) | ||
| 53 | } | ||
| 54 | RefnameValidation{err: git_validate::reference::name::Error, path: BString} { | ||
| 55 | display("The path to a symbolic reference is invalid") | ||
| 56 | source(err) | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | impl TryFrom<MaybeUnsafeState> for State { | ||
| 62 | type Error = Error; | ||
| 63 | |||
| 64 | fn try_from(v: MaybeUnsafeState) -> Result<Self, Self::Error> { | ||
| 65 | Ok(match v { | ||
| 66 | MaybeUnsafeState::Id(id) => State::Id(id), | ||
| 67 | MaybeUnsafeState::UnvalidatedPath(path) => State::Path(match git_validate::refname(path.as_ref()) { | ||
| 68 | Err(err) => return Err(Error::RefnameValidation { err, path }), | ||
| 69 | Ok(_) => path, | ||
| 70 | }), | ||
| 71 | }) | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl<'a> Reference<'a> { | ||
| 76 | pub fn try_from_path( | ||
| 77 | parent: &'a Store, | ||
| 78 | relative_path: impl Into<PathBuf>, | ||
| 79 | path_contents: &[u8], | ||
| 80 | ) -> Result<Self, Error> { | ||
| 81 | Ok(Reference { | ||
| 82 | parent, | ||
| 83 | relative_path: relative_path.into(), | ||
| 84 | state: parse(path_contents) | ||
| 85 | .map_err(|_| Error::Parse(path_contents.into()))? | ||
| 86 | .1 | ||
| 87 | .try_into()?, | ||
| 88 | }) | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | fn is_hex_digit_lc(b: u8) -> bool { | ||
| 93 | matches!(b, b'0'..=b'9' | b'a'..=b'f') | ||
| 94 | } | ||
| 95 | |||
| 96 | fn hex_sha1(i: &[u8]) -> IResult<&[u8], &[u8]> { | ||
| 97 | take_while_m_n(40usize, 40, is_hex_digit_lc)(i) | ||
| 98 | } | ||
| 99 | |||
| 100 | fn newline(i: &[u8]) -> IResult<&[u8], &[u8]> { | ||
| 101 | alt((tag(b"\r\n"), tag(b"\n")))(i) | ||
| 102 | } | ||
| 103 | |||
| 104 | fn parse(bytes: &[u8]) -> IResult<&[u8], MaybeUnsafeState> { | ||
| 105 | let is_space = |b: u8| b == b' '; | ||
| 106 | if let (path, Some(_ref_prefix)) = opt(terminated(tag("ref: "), take_while(is_space)))(bytes)? { | ||
| 107 | map(terminated(take_while(|b| b != b'\r' && b != b'\n'), newline), |path| { | ||
| 108 | MaybeUnsafeState::UnvalidatedPath(path.into()) | ||
| 109 | })(path) | ||
| 110 | } else { | ||
| 111 | map(terminated(hex_sha1, newline), |hex| { | ||
| 112 | MaybeUnsafeState::Id(ObjectId::from_hex(hex).expect("prior validation")) | ||
| 113 | })(bytes) | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||