rust/hg-core/src/revlog/manifest.rs
changeset 48343 eb428010aad2
parent 48342 10c32e1b892a
child 48495 e293ff808a05
equal deleted inserted replaced
48342:10c32e1b892a 48343:eb428010aad2
     2 use crate::repo::Repo;
     2 use crate::repo::Repo;
     3 use crate::revlog::revlog::{Revlog, RevlogError};
     3 use crate::revlog::revlog::{Revlog, RevlogError};
     4 use crate::revlog::Revision;
     4 use crate::revlog::Revision;
     5 use crate::revlog::{Node, NodePrefix};
     5 use crate::revlog::{Node, NodePrefix};
     6 use crate::utils::hg_path::HgPath;
     6 use crate::utils::hg_path::HgPath;
       
     7 use crate::utils::SliceExt;
     7 
     8 
     8 /// A specialized `Revlog` to work with `manifest` data format.
     9 /// A specialized `Revlog` to work with `manifest` data format.
     9 pub struct Manifestlog {
    10 pub struct Manifestlog {
    10     /// The generic `revlog` format.
    11     /// The generic `revlog` format.
    11     revlog: Revlog,
    12     revlog: Revlog,
    53 pub struct Manifest {
    54 pub struct Manifest {
    54     bytes: Vec<u8>,
    55     bytes: Vec<u8>,
    55 }
    56 }
    56 
    57 
    57 impl Manifest {
    58 impl Manifest {
    58     /// Return an iterator over the lines of the entry.
    59     pub fn iter(
    59     pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
    60         &self,
       
    61     ) -> impl Iterator<Item = Result<ManifestEntry, HgError>> {
    60         self.bytes
    62         self.bytes
    61             .split(|b| b == &b'\n')
    63             .split(|b| b == &b'\n')
    62             .filter(|line| !line.is_empty())
    64             .filter(|line| !line.is_empty())
    63     }
    65             .map(|line| {
    64 
    66                 let (path, rest) = line.split_2(b'\0').ok_or_else(|| {
    65     /// Return an iterator over the files of the entry.
       
    66     pub fn files(&self) -> impl Iterator<Item = Result<&HgPath, HgError>> {
       
    67         self.lines().filter(|line| !line.is_empty()).map(|line| {
       
    68             let pos =
       
    69                 line.iter().position(|x| x == &b'\0').ok_or_else(|| {
       
    70                     HgError::corrupted("manifest line should contain \\0")
    67                     HgError::corrupted("manifest line should contain \\0")
    71                 })?;
    68                 })?;
    72             Ok(HgPath::new(&line[..pos]))
    69                 let path = HgPath::new(path);
    73         })
    70                 let (hex_node_id, flags) = match rest.split_last() {
    74     }
    71                     Some((&b'x', rest)) => (rest, Some(b'x')),
    75 
    72                     Some((&b'l', rest)) => (rest, Some(b'l')),
    76     /// Return an iterator over the files of the entry.
    73                     Some((&b't', rest)) => (rest, Some(b't')),
    77     pub fn files_with_nodes(
    74                     _ => (rest, None),
    78         &self,
    75                 };
    79     ) -> impl Iterator<Item = Result<(&HgPath, &[u8]), HgError>> {
    76                 Ok(ManifestEntry {
    80         self.lines().filter(|line| !line.is_empty()).map(|line| {
    77                     path,
    81             let pos =
    78                     hex_node_id,
    82                 line.iter().position(|x| x == &b'\0').ok_or_else(|| {
    79                     flags,
    83                     HgError::corrupted("manifest line should contain \\0")
    80                 })
    84                 })?;
    81             })
    85             let hash_start = pos + 1;
       
    86             let hash_end = hash_start + 40;
       
    87             Ok((HgPath::new(&line[..pos]), &line[hash_start..hash_end]))
       
    88         })
       
    89     }
    82     }
    90 
    83 
    91     /// If the given path is in this manifest, return its filelog node ID
    84     /// If the given path is in this manifest, return its filelog node ID
    92     pub fn find_file(&self, path: &HgPath) -> Result<Option<Node>, HgError> {
    85     pub fn find_file(
       
    86         &self,
       
    87         path: &HgPath,
       
    88     ) -> Result<Option<ManifestEntry>, HgError> {
    93         // TODO: use binary search instead of linear scan. This may involve
    89         // TODO: use binary search instead of linear scan. This may involve
    94         // building (and caching) an index of the byte indicex of each manifest
    90         // building (and caching) an index of the byte indicex of each manifest
    95         // line.
    91         // line.
    96         for entry in self.files_with_nodes() {
    92 
    97             let (manifest_path, node) = entry?;
    93         // TODO: use try_find when available (if still using linear scan)
    98             if manifest_path == path {
    94         // https://github.com/rust-lang/rust/issues/63178
    99                 return Ok(Some(Node::from_hex_for_repo(node)?));
    95         for entry in self.iter() {
       
    96             let entry = entry?;
       
    97             if entry.path == path {
       
    98                 return Ok(Some(entry));
   100             }
    99             }
   101         }
   100         }
   102         Ok(None)
   101         Ok(None)
   103     }
   102     }
   104 }
   103 }
       
   104 
       
   105 /// `Manifestlog` entry which knows how to interpret the `manifest` data bytes.
       
   106 #[derive(Debug)]
       
   107 pub struct ManifestEntry<'manifest> {
       
   108     pub path: &'manifest HgPath,
       
   109     pub hex_node_id: &'manifest [u8],
       
   110 
       
   111     /// `Some` values are b'x', b'l', or 't'
       
   112     pub flags: Option<u8>,
       
   113 }
       
   114 
       
   115 impl ManifestEntry<'_> {
       
   116     pub fn node_id(&self) -> Result<Node, HgError> {
       
   117         Node::from_hex_for_repo(self.hex_node_id)
       
   118     }
       
   119 }