rust/hg-cpython/src/parsers.rs
changeset 42747 760a7851e9ba
parent 42746 b3518b0baa47
child 42748 7cae6bc29ff9
equal deleted inserted replaced
42746:b3518b0baa47 42747:760a7851e9ba
       
     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 //!
       
    13 use cpython::{
       
    14     exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
       
    15     PythonObject, ToPyObject,
       
    16 };
       
    17 use hg::{
       
    18     pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
       
    19     DirstatePackError, DirstateParents, DirstateParseError,
       
    20 };
       
    21 use std::collections::HashMap;
       
    22 
       
    23 use libc::c_char;
       
    24 
       
    25 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate_vec};
       
    26 
       
    27 fn parse_dirstate_wrapper(
       
    28     py: Python,
       
    29     dmap: PyDict,
       
    30     copymap: PyDict,
       
    31     st: PyBytes,
       
    32 ) -> PyResult<PyTuple> {
       
    33     match parse_dirstate(st.data(py)) {
       
    34         Ok((parents, dirstate_vec, copies)) => {
       
    35             for (filename, entry) in dirstate_vec {
       
    36                 dmap.set_item(
       
    37                     py,
       
    38                     PyBytes::new(py, &filename[..]),
       
    39                     decapsule_make_dirstate_tuple(py)?(
       
    40                         entry.state as c_char,
       
    41                         entry.mode,
       
    42                         entry.size,
       
    43                         entry.mtime,
       
    44                     ),
       
    45                 )?;
       
    46             }
       
    47             for CopyVecEntry { path, copy_path } in copies {
       
    48                 copymap.set_item(
       
    49                     py,
       
    50                     PyBytes::new(py, path),
       
    51                     PyBytes::new(py, copy_path),
       
    52                 )?;
       
    53             }
       
    54             Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
       
    55                 .to_py_object(py))
       
    56         }
       
    57         Err(e) => Err(PyErr::new::<exc::ValueError, _>(
       
    58             py,
       
    59             match e {
       
    60                 DirstateParseError::TooLittleData => {
       
    61                     "too little data for parents".to_string()
       
    62                 }
       
    63                 DirstateParseError::Overflow => {
       
    64                     "overflow in dirstate".to_string()
       
    65                 }
       
    66                 DirstateParseError::CorruptedEntry(e) => e,
       
    67             },
       
    68         )),
       
    69     }
       
    70 }
       
    71 
       
    72 fn pack_dirstate_wrapper(
       
    73     py: Python,
       
    74     dmap: PyDict,
       
    75     copymap: PyDict,
       
    76     pl: PyTuple,
       
    77     now: PyInt,
       
    78 ) -> PyResult<PyBytes> {
       
    79     let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
       
    80     let p1: &[u8] = p1.data(py);
       
    81     let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
       
    82     let p2: &[u8] = p2.data(py);
       
    83 
       
    84     let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
       
    85 
       
    86     let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
       
    87         .items(py)
       
    88         .iter()
       
    89         .map(|(key, value)| {
       
    90             Ok((
       
    91                 key.extract::<PyBytes>(py)?.data(py).to_owned(),
       
    92                 value.extract::<PyBytes>(py)?.data(py).to_owned(),
       
    93             ))
       
    94         })
       
    95         .collect();
       
    96 
       
    97     match pack_dirstate(
       
    98         &dirstate_vec,
       
    99         &copies?,
       
   100         DirstateParents { p1, p2 },
       
   101         now.as_object().extract::<i32>(py)?,
       
   102     ) {
       
   103         Ok((packed, new_dirstate_vec)) => {
       
   104             for (
       
   105                 filename,
       
   106                 DirstateEntry {
       
   107                     state,
       
   108                     mode,
       
   109                     size,
       
   110                     mtime,
       
   111                 },
       
   112             ) in new_dirstate_vec
       
   113             {
       
   114                 dmap.set_item(
       
   115                     py,
       
   116                     PyBytes::new(py, &filename[..]),
       
   117                     decapsule_make_dirstate_tuple(py)?(
       
   118                         state as c_char,
       
   119                         mode,
       
   120                         size,
       
   121                         mtime,
       
   122                     ),
       
   123                 )?;
       
   124             }
       
   125             Ok(PyBytes::new(py, &packed))
       
   126         }
       
   127         Err(error) => Err(PyErr::new::<exc::ValueError, _>(
       
   128             py,
       
   129             match error {
       
   130                 DirstatePackError::CorruptedParent => {
       
   131                     "expected a 20-byte hash".to_string()
       
   132                 }
       
   133                 DirstatePackError::CorruptedEntry(e) => e,
       
   134                 DirstatePackError::BadSize(expected, actual) => {
       
   135                     format!("bad dirstate size: {} != {}", actual, expected)
       
   136                 }
       
   137             },
       
   138         )),
       
   139     }
       
   140 }
       
   141 
       
   142 /// Create the module, with `__package__` given from parent
       
   143 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
       
   144     let dotted_name = &format!("{}.parsers", package);
       
   145     let m = PyModule::new(py, dotted_name)?;
       
   146 
       
   147     m.add(py, "__package__", package)?;
       
   148     m.add(py, "__doc__", "Parsers - Rust implementation")?;
       
   149 
       
   150     m.add(
       
   151         py,
       
   152         "parse_dirstate",
       
   153         py_fn!(
       
   154             py,
       
   155             parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
       
   156         ),
       
   157     )?;
       
   158     m.add(
       
   159         py,
       
   160         "pack_dirstate",
       
   161         py_fn!(
       
   162             py,
       
   163             pack_dirstate_wrapper(
       
   164                 dmap: PyDict,
       
   165                 copymap: PyDict,
       
   166                 pl: PyTuple,
       
   167                 now: PyInt
       
   168             )
       
   169         ),
       
   170     )?;
       
   171 
       
   172     let sys = PyModule::import(py, "sys")?;
       
   173     let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
       
   174     sys_modules.set_item(py, dotted_name, &m)?;
       
   175 
       
   176     Ok(m)
       
   177 }