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 | 11 | pub use borrowed::oid; |
| 12 | 12 | |
| 13 | 13 | mod owned; |
| 14 | −pub use owned::{ObjectId, Prefix}; | |
| 14 | +pub use owned::{prefix, ObjectId, Prefix}; | |
| 15 | 15 | |
| 16 | 16 | #[allow(missing_docs)] |
| 17 | 17 | pub mod decode { |
| @@ -11,17 +11,40 @@ pub struct Prefix { · 10 unmodified lines | ||
| 11 | 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 | 33 | impl Prefix { |
| 15 | 34 | /// Create a new instance by taking a full `id` as input and truncating it to `hex_len`. |
| 16 | 35 | /// |
| 17 | 36 | /// For instance, with `hex_len` of 7 the resulting prefix is 3.5 bytes, or 3 bytes and 4 bits |
| 18 | 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 | 39 | let id = id.as_ref(); |
| 21 | − assert!( | |
| 22 | − hex_len <= id.kind().len_in_hex(), | |
| 23 | − "hex_len must not be larger than the maximum hex len of the input id" | |
| 24 | − ); | |
| 40 | + if hex_len > id.kind().len_in_hex() { | |
| 41 | + return Err(prefix::Error::TooLong { | |
| 42 | + object_kind: id.kind(), | |
| 43 | + hex_len, | |
| 44 | + }); | |
| 45 | + } else if hex_len < 4 { | |
| 46 | + return Err(prefix::Error::TooShort { hex_len }); | |
| 47 | + } | |
| 25 | 48 | |
| 26 | 49 | let mut prefix = ObjectId::null(id.kind()); |
| 27 | 50 | let b = prefix.as_mut_slice(); |
| @@ -31,7 +54,7 @@ impl Prefix { · 3 unmodified lines | ||
| 31 | 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 | 60 | /// Returns the prefix as object id. |
| @@ -8,23 +8,32 @@ mod prefix { · 7 unmodified lines | ||
| 8 | 8 | let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd"; |
| 9 | 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())); | |
| 12 | − | |
| 13 | − for hex_len in 1..oid.kind().len_in_hex() { | |
| 11 | + for hex_len in 4..oid.kind().len_in_hex() { | |
| 14 | 12 | let mut expected = String::from(&oid_hex[..hex_len]); |
| 15 | 13 | let num_of_zeros = oid.kind().len_in_hex() - hex_len; |
| 16 | 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 | 16 | assert_eq!(prefix.as_oid().to_hex().to_string(), expected, "{}", hex_len); |
| 19 | 17 | assert_eq!(prefix.hex_len(), hex_len); |
| 20 | 18 | } |
| 21 | 19 | } |
| 22 | 20 | |
| 23 | 21 | #[test] |
| 24 | − #[should_panic] | |
| 25 | − fn panics_if_hex_len_is_longer_than_oid_len_in_hex() { | |
| 22 | + fn errors_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 | 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 | 106 | /// The type for referring to indices of an entry within the index file. |
| 103 | 107 | pub type EntryIndex = u32; |
| 104 | 108 | |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | use std::path::{Path, PathBuf}; |
| 2 | 2 | |
| 3 | +use crate::index::PrefixLookupResult; | |
| 3 | 4 | use crate::{ |
| 4 | 5 | data, |
| 5 | 6 | multi_index::{EntryIndex, File, PackIndex, Version}, |
| @@ -67,7 +68,7 @@ impl File { · 61 unmodified lines | ||
| 67 | 68 | } |
| 68 | 69 | |
| 69 | 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 | 72 | todo!() |
| 72 | 73 | } |
| 73 | 74 | |
| @@ -16,15 +16,23 @@ mod access { · 15 unmodified lines | ||
| 16 | 16 | use std::path::PathBuf; |
| 17 | 17 | |
| 18 | 18 | #[test] |
| 19 | − fn lookup_abbrev() { | |
| 19 | + #[ignore] | |
| 20 | + fn lookup_with_ambiguity() {} | |
| 21 | + | |
| 22 | + #[test] | |
| 23 | + fn lookup_prefix() { | |
| 20 | 24 | let (file, _path) = multi_index(); |
| 21 | 25 | |
| 22 | 26 | for (idx, entry) in file.iter().enumerate() { |
| 23 | 27 | let hex_len = (idx % file.object_hash().len_in_hex()).min(7); |
| 24 | 28 | let hex_oid = entry.oid.to_hex_with_len(hex_len).to_string(); |
| 25 | 29 | assert_eq!(hex_oid.len(), hex_len); |
| 26 | − let _oid_prefix = git_hash::Prefix::from_id(&entry.oid, hex_len); | |
| 27 | − // file.lookup_prefix(oid_prefix, hex_len).expect("non-ambiguous") | |
| 30 | + let oid_prefix = git_hash::Prefix::try_from_id(&entry.oid, hex_len); | |
| 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 | |