That way, min and max lengths is assumed to be correct.
Unified · Split
| @@ -11,7 +11,7 @@ use std::{convert::TryFrom, str::FromStr}; · 10 unmodified lines | |||
| 11 | pub use borrowed::oid; | 11 | pub use borrowed::oid; |
| 12 | 12 | ||
| 13 | mod owned; | 13 | mod owned; |
| 14 | pub use owned::{ObjectId, Prefix}; | 14 | pub use owned::{prefix, ObjectId, Prefix}; |
| 15 | 15 | ||
| 16 | #[allow(missing_docs)] | 16 | #[allow(missing_docs)] |
| 17 | pub mod decode { | 17 | pub mod decode { |
| @@ -11,17 +11,40 @@ pub struct Prefix { · 10 unmodified lines | |||
| 11 | hex_len: usize, | 11 | hex_len: usize, |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | /// | ||
| 15 | pub mod prefix { | ||
| 16 | use quick_error::quick_error; | ||
| 17 | |||
| 18 | quick_error! { | ||
| 19 | /// TODO: | ||
| 20 | #[derive(Debug)] | ||
| 21 | #[allow(missing_docs)] | ||
| 22 | pub enum Error { | ||
| 23 | TooShort { hex_len: usize } { | ||
| 24 | display("The minimum hex length of a short object id is 4, got {}", hex_len) | ||
| 25 | } | ||
| 26 | TooLong { object_kind: crate::Kind, hex_len: usize } { | ||
| 27 | display("An object of kind {} cannot be larger than {} in hex, but {} was requested", object_kind, object_kind.len_in_hex(), hex_len) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 14 | impl Prefix { | 33 | impl Prefix { |
| 15 | /// Create a new instance by taking a full `id` as input and truncating it to `hex_len`. | 34 | /// Create a new instance by taking a full `id` as input and truncating it to `hex_len`. |
| 16 | /// | 35 | /// |
| 17 | /// For instance, with `hex_len` of 7 the resulting prefix is 3.5 bytes, or 3 bytes and 4 bits | 36 | /// For instance, with `hex_len` of 7 the resulting prefix is 3.5 bytes, or 3 bytes and 4 bits |
| 18 | /// wide, with all other bytes and bits set to zero. | 37 | /// wide, with all other bytes and bits set to zero. |
| 19 | pub fn from_id(id: impl AsRef<oid>, hex_len: usize) -> Self { | 38 | pub fn try_from_id(id: impl AsRef<oid>, hex_len: usize) -> Result<Self, prefix::Error> { |
| 20 | let id = id.as_ref(); | 39 | let id = id.as_ref(); |
| 21 | assert!( | 40 | if hex_len > id.kind().len_in_hex() { |
| 22 | hex_len <= id.kind().len_in_hex(), | 41 | return Err(prefix::Error::TooLong { |
| 23 | "hex_len must not be larger than the maximum hex len of the input id" | 42 | object_kind: id.kind(), |
| 24 | ); | 43 | hex_len, |
| 44 | }); | ||
| 45 | } else if hex_len < 4 { | ||
| 46 | return Err(prefix::Error::TooShort { hex_len }); | ||
| 47 | } | ||
| 25 | 48 | ||
| 26 | let mut prefix = ObjectId::null(id.kind()); | 49 | let mut prefix = ObjectId::null(id.kind()); |
| 27 | let b = prefix.as_mut_slice(); | 50 | let b = prefix.as_mut_slice(); |
| @@ -31,7 +54,7 @@ impl Prefix { · 3 unmodified lines | |||
| 31 | b[hex_len / 2] &= 0xf0; | 54 | b[hex_len / 2] &= 0xf0; |
| 32 | } | 55 | } |
| 33 | 56 | ||
| 34 | Prefix { bytes: prefix, hex_len } | 57 | Ok(Prefix { bytes: prefix, hex_len }) |
| 35 | } | 58 | } |
| 36 | 59 | ||
| 37 | /// Returns the prefix as object id. | 60 | /// Returns the prefix as object id. |
| @@ -8,23 +8,32 @@ mod prefix { · 7 unmodified lines | |||
| 8 | let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd"; | 8 | let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd"; |
| 9 | let oid = hex_to_id(oid_hex); | 9 | let oid = hex_to_id(oid_hex); |
| 10 | 10 | ||
| 11 | assert_eq!(git_hash::Prefix::from_id(oid, 0).as_oid(), ObjectId::null(oid.kind())); | 11 | for hex_len in 4..oid.kind().len_in_hex() { |
| 12 | |||
| 13 | for hex_len in 1..oid.kind().len_in_hex() { | ||
| 14 | let mut expected = String::from(&oid_hex[..hex_len]); | 12 | let mut expected = String::from(&oid_hex[..hex_len]); |
| 15 | let num_of_zeros = oid.kind().len_in_hex() - hex_len; | 13 | let num_of_zeros = oid.kind().len_in_hex() - hex_len; |
| 16 | expected.extend(std::iter::repeat('0').take(num_of_zeros)); | 14 | expected.extend(std::iter::repeat('0').take(num_of_zeros)); |
| 17 | let prefix = git_hash::Prefix::from_id(oid, hex_len); | 15 | let prefix = git_hash::Prefix::try_from_id(oid, hex_len).unwrap(); |
| 18 | assert_eq!(prefix.as_oid().to_hex().to_string(), expected, "{}", hex_len); | 16 | assert_eq!(prefix.as_oid().to_hex().to_string(), expected, "{}", hex_len); |
| 19 | assert_eq!(prefix.hex_len(), hex_len); | 17 | assert_eq!(prefix.hex_len(), hex_len); |
| 20 | } | 18 | } |
| 21 | } | 19 | } |
| 22 | 20 | ||
| 23 | #[test] | 21 | #[test] |
| 24 | #[should_panic] | 22 | fn errors_if_hex_len_is_longer_than_oid_len_in_hex() { |
| 25 | fn panics_if_hex_len_is_longer_than_oid_len_in_hex() { | 23 | let kind = Kind::Sha1; |
| 24 | assert!(matches!( | ||
| 25 | git_hash::Prefix::try_from_id(ObjectId::null(kind), kind.len_in_hex() + 1), | ||
| 26 | Err(git_hash::prefix::Error::TooLong { .. }) | ||
| 27 | )); | ||
| 28 | } | ||
| 29 | |||
| 30 | #[test] | ||
| 31 | fn errors_if_hex_len_is_too_short() { | ||
| 26 | let kind = Kind::Sha1; | 32 | let kind = Kind::Sha1; |
| 27 | git_hash::Prefix::from_id(ObjectId::null(kind), kind.len_in_hex() + 1); | 33 | assert!(matches!( |
| 34 | git_hash::Prefix::try_from_id(ObjectId::null(kind), 3), | ||
| 35 | Err(git_hash::prefix::Error::TooShort { .. }) | ||
| 36 | )); | ||
| 28 | } | 37 | } |
| 29 | } | 38 | } |
| 30 | } | 39 | } |
| @@ -99,6 +99,10 @@ impl Version { · 98 unmodified lines | |||
| 99 | } | 99 | } |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | /// A way to indicate if a lookup, despite successful, was ambiguous or yielded exactly | ||
| 103 | /// one result in the particular index. | ||
| 104 | pub type PrefixLookupResult = Result<EntryIndex, ()>; | ||
| 105 | |||
| 102 | /// The type for referring to indices of an entry within the index file. | 106 | /// The type for referring to indices of an entry within the index file. |
| 103 | pub type EntryIndex = u32; | 107 | pub type EntryIndex = u32; |
| 104 | 108 | ||
| @@ -1,5 +1,6 @@ | |||
| 1 | use std::path::{Path, PathBuf}; | 1 | use std::path::{Path, PathBuf}; |
| 2 | 2 | ||
| 3 | use crate::index::PrefixLookupResult; | ||
| 3 | use crate::{ | 4 | use crate::{ |
| 4 | data, | 5 | data, |
| 5 | multi_index::{EntryIndex, File, PackIndex, Version}, | 6 | multi_index::{EntryIndex, File, PackIndex, Version}, |
| @@ -67,7 +68,7 @@ impl File { · 61 unmodified lines | |||
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | /// TODO | 70 | /// TODO |
| 70 | pub fn lookup_prefix(&self, _id: impl AsRef<git_hash::oid>, _hex_len: usize) -> Option<EntryIndex> { | 71 | pub fn lookup_prefix(&self, prefix: git_hash::Prefix) -> Option<PrefixLookupResult> { |
| 71 | todo!() | 72 | todo!() |
| 72 | } | 73 | } |
| 73 | 74 | ||
| @@ -16,15 +16,23 @@ mod access { · 15 unmodified lines | |||
| 16 | use std::path::PathBuf; | 16 | use std::path::PathBuf; |
| 17 | 17 | ||
| 18 | #[test] | 18 | #[test] |
| 19 | fn lookup_abbrev() { | 19 | #[ignore] |
| 20 | fn lookup_with_ambiguity() {} | ||
| 21 | |||
| 22 | #[test] | ||
| 23 | fn lookup_prefix() { | ||
| 20 | let (file, _path) = multi_index(); | 24 | let (file, _path) = multi_index(); |
| 21 | 25 | ||
| 22 | for (idx, entry) in file.iter().enumerate() { | 26 | for (idx, entry) in file.iter().enumerate() { |
| 23 | let hex_len = (idx % file.object_hash().len_in_hex()).min(7); | 27 | let hex_len = (idx % file.object_hash().len_in_hex()).min(7); |
| 24 | let hex_oid = entry.oid.to_hex_with_len(hex_len).to_string(); | 28 | let hex_oid = entry.oid.to_hex_with_len(hex_len).to_string(); |
| 25 | assert_eq!(hex_oid.len(), hex_len); | 29 | assert_eq!(hex_oid.len(), hex_len); |
| 26 | let _oid_prefix = git_hash::Prefix::from_id(&entry.oid, hex_len); | 30 | let oid_prefix = git_hash::Prefix::try_from_id(&entry.oid, hex_len); |
| 27 | // file.lookup_prefix(oid_prefix, hex_len).expect("non-ambiguous") | 31 | let entry_index = file |
| 32 | .lookup_prefix(oid_prefix) | ||
| 33 | .expect("object found") | ||
| 34 | .expect("non-ambiguous"); | ||
| 35 | assert_eq!(file.oid_at_index(entry_index), entry.oid); | ||
| 28 | } | 36 | } |
| 29 | } | 37 | } |
| 30 | 38 | ||