rust/hg-cpython/src/parsers.rs
changeset 48020 1194394510ba
parent 48019 a83e24c3af6b
child 48021 627cd8f33db0
equal deleted inserted replaced
48019:a83e24c3af6b 48020:1194394510ba
     1 // parsers.rs
       
     2 //
       
     3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
       
     4 //
       
     5 // This software may be used and distributed according to the terms of the
       
     6 // GNU General Public License version 2 or any later version.
       
     7 
       
     8 //! Bindings for the `hg::dirstate::parsers` module provided by the
       
     9 //! `hg-core` package.
       
    10 //!
       
    11 //! From Python, this will be seen as `mercurial.rustext.parsers`
       
    12 use cpython::{
       
    13     exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
       
    14     PythonObject, ToPyObject,
       
    15 };
       
    16 use hg::{
       
    17     dirstate::parsers::Timestamp, pack_dirstate, parse_dirstate,
       
    18     utils::hg_path::HgPathBuf, DirstateEntry, DirstateParents, FastHashMap,
       
    19     PARENT_SIZE,
       
    20 };
       
    21 use std::convert::TryInto;
       
    22 
       
    23 use crate::dirstate::{extract_dirstate, make_dirstate_item};
       
    24 
       
    25 fn parse_dirstate_wrapper(
       
    26     py: Python,
       
    27     dmap: PyDict,
       
    28     copymap: PyDict,
       
    29     st: PyBytes,
       
    30 ) -> PyResult<PyTuple> {
       
    31     match parse_dirstate(st.data(py)) {
       
    32         Ok((parents, entries, copies)) => {
       
    33             let dirstate_map: FastHashMap<HgPathBuf, DirstateEntry> = entries
       
    34                 .into_iter()
       
    35                 .map(|(path, entry)| (path.to_owned(), entry))
       
    36                 .collect();
       
    37             let copy_map: FastHashMap<HgPathBuf, HgPathBuf> = copies
       
    38                 .into_iter()
       
    39                 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
       
    40                 .collect();
       
    41 
       
    42             for (filename, entry) in &dirstate_map {
       
    43                 dmap.set_item(
       
    44                     py,
       
    45                     PyBytes::new(py, filename.as_bytes()),
       
    46                     make_dirstate_item(py, entry)?,
       
    47                 )?;
       
    48             }
       
    49             for (path, copy_path) in copy_map {
       
    50                 copymap.set_item(
       
    51                     py,
       
    52                     PyBytes::new(py, path.as_bytes()),
       
    53                     PyBytes::new(py, copy_path.as_bytes()),
       
    54                 )?;
       
    55             }
       
    56             Ok(dirstate_parents_to_pytuple(py, parents))
       
    57         }
       
    58         Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
       
    59     }
       
    60 }
       
    61 
       
    62 fn pack_dirstate_wrapper(
       
    63     py: Python,
       
    64     dmap: PyDict,
       
    65     copymap: PyDict,
       
    66     pl: PyTuple,
       
    67     now: PyInt,
       
    68 ) -> PyResult<PyBytes> {
       
    69     let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
       
    70     let p1: &[u8] = p1.data(py);
       
    71     let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
       
    72     let p2: &[u8] = p2.data(py);
       
    73 
       
    74     let mut dirstate_map = extract_dirstate(py, &dmap)?;
       
    75 
       
    76     let copies: Result<FastHashMap<HgPathBuf, HgPathBuf>, PyErr> = copymap
       
    77         .items(py)
       
    78         .iter()
       
    79         .map(|(key, value)| {
       
    80             Ok((
       
    81                 HgPathBuf::from_bytes(key.extract::<PyBytes>(py)?.data(py)),
       
    82                 HgPathBuf::from_bytes(value.extract::<PyBytes>(py)?.data(py)),
       
    83             ))
       
    84         })
       
    85         .collect();
       
    86 
       
    87     if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
       
    88         return Err(PyErr::new::<exc::ValueError, _>(
       
    89             py,
       
    90             "expected a 20-byte hash".to_string(),
       
    91         ));
       
    92     }
       
    93 
       
    94     match pack_dirstate(
       
    95         &mut dirstate_map,
       
    96         &copies?,
       
    97         DirstateParents {
       
    98             p1: p1.try_into().unwrap(),
       
    99             p2: p2.try_into().unwrap(),
       
   100         },
       
   101         Timestamp(now.as_object().extract::<i64>(py)?),
       
   102     ) {
       
   103         Ok(packed) => {
       
   104             for (filename, entry) in dirstate_map.iter() {
       
   105                 dmap.set_item(
       
   106                     py,
       
   107                     PyBytes::new(py, filename.as_bytes()),
       
   108                     make_dirstate_item(py, &entry)?,
       
   109                 )?;
       
   110             }
       
   111             Ok(PyBytes::new(py, &packed))
       
   112         }
       
   113         Err(error) => {
       
   114             Err(PyErr::new::<exc::ValueError, _>(py, error.to_string()))
       
   115         }
       
   116     }
       
   117 }
       
   118 
       
   119 /// Create the module, with `__package__` given from parent
       
   120 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
       
   121     let dotted_name = &format!("{}.parsers", package);
       
   122     let m = PyModule::new(py, dotted_name)?;
       
   123 
       
   124     m.add(py, "__package__", package)?;
       
   125     m.add(py, "__doc__", "Parsers - Rust implementation")?;
       
   126 
       
   127     m.add(
       
   128         py,
       
   129         "parse_dirstate",
       
   130         py_fn!(
       
   131             py,
       
   132             parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
       
   133         ),
       
   134     )?;
       
   135     m.add(
       
   136         py,
       
   137         "pack_dirstate",
       
   138         py_fn!(
       
   139             py,
       
   140             pack_dirstate_wrapper(
       
   141                 dmap: PyDict,
       
   142                 copymap: PyDict,
       
   143                 pl: PyTuple,
       
   144                 now: PyInt
       
   145             )
       
   146         ),
       
   147     )?;
       
   148 
       
   149     let sys = PyModule::import(py, "sys")?;
       
   150     let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
       
   151     sys_modules.set_item(py, dotted_name, &m)?;
       
   152 
       
   153     Ok(m)
       
   154 }
       
   155 
       
   156 pub(crate) fn dirstate_parents_to_pytuple(
       
   157     py: Python,
       
   158     parents: &DirstateParents,
       
   159 ) -> PyTuple {
       
   160     let p1 = PyBytes::new(py, parents.p1.as_bytes());
       
   161     let p2 = PyBytes::new(py, parents.p2.as_bytes());
       
   162     (p1, p2).to_py_object(py)
       
   163 }