1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Operations between repositories.
use crate::error::Error;
use crate::repository::{GenericRepository, PyRepository};
use crate::RevisionId;
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyDict};
use std::collections::HashMap;
/// Trait for types that can be converted to Python InterRepository objects.
///
/// This trait is implemented by types that represent a Breezy InterRepository,
/// which handles operations between repositories.
pub trait PyInterRepository: for<'py> IntoPyObject<'py> + std::any::Any + std::fmt::Debug {
/// Get the underlying Python object for this inter-repository.
fn to_object(&self, py: Python<'_>) -> Py<PyAny>;
}
/// Generic wrapper for a Python InterRepository object.
///
/// This struct provides a Rust interface to a Breezy InterRepository object.
pub struct GenericInterRepository(Py<PyAny>);
impl<'py> IntoPyObject<'py> for GenericInterRepository {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = std::convert::Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.0.into_bound(py))
}
}
impl<'a, 'py> FromPyObject<'a, 'py> for GenericInterRepository {
type Error = PyErr;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
Ok(GenericInterRepository(obj.to_owned().unbind()))
}
}
impl PyInterRepository for GenericInterRepository {
fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
self.0.clone_ref(py)
}
}
impl GenericInterRepository {
/// Create a new GenericInterRepository from a Python object.
///
/// # Arguments
///
/// * `obj` - The Python object representing a Breezy InterRepository
///
/// # Returns
///
/// A new GenericInterRepository wrapping the provided Python object
pub fn new(obj: Py<PyAny>) -> Self {
Self(obj)
}
}
impl std::fmt::Debug for GenericInterRepository {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_fmt(format_args!("GenericInterRepository({:?})", self.0))
}
}
/// Get an InterRepository for operations between two repositories.
///
/// # Arguments
///
/// * `source` - The source repository
/// * `target` - The target repository
///
/// # Returns
///
/// A boxed InterRepository trait object that can perform operations between the repositories
///
/// # Errors
///
/// Returns an error if the operation fails, such as if the repositories are incompatible
pub fn get<S: PyRepository, T: PyRepository>(
source: &S,
target: &T,
) -> Result<Box<dyn InterRepository>, Error> {
Python::attach(|py| {
let m = py.import("breezy.repository")?;
let interrepo = m.getattr("InterRepository")?;
let inter_repository =
interrepo.call_method1("get", (source.to_object(py), target.to_object(py)))?;
Ok(
Box::new(GenericInterRepository::new(inter_repository.unbind()))
as Box<dyn InterRepository>,
)
})
}
/// Trait for operations between repositories.
///
/// This trait defines the operations that can be performed between two repositories,
/// such as fetching revisions from one repository to another.
pub trait InterRepository: std::fmt::Debug {
/// Get the source repository.
///
/// # Returns
///
/// The source repository
fn get_source(&self) -> GenericRepository;
/// Get the target repository.
///
/// # Returns
///
/// The target repository
fn get_target(&self) -> GenericRepository;
/// Fetch references from the source repository to the target repository.
///
/// # Arguments
///
/// * `get_changed_refs` - A mutex-protected function to get the references to fetch
/// * `lossy` - If true, lossy conversion is allowed
/// * `overwrite` - If true, existing references can be overwritten
///
/// # Returns
///
/// Ok(()) on success, or an error if the operation fails
// TODO: This should really be on InterGitRepository
fn fetch_refs(
&self,
get_changed_refs: std::sync::Mutex<
Box<
dyn FnMut(
&HashMap<Vec<u8>, (Vec<u8>, Option<RevisionId>)>,
) -> HashMap<Vec<u8>, (Vec<u8>, Option<RevisionId>)>
+ Send,
>,
>,
lossy: bool,
overwrite: bool,
) -> Result<(), Error>;
}
impl<T: PyInterRepository> InterRepository for T {
fn get_source(&self) -> GenericRepository {
Python::attach(|py| -> PyResult<GenericRepository> {
let source = self.to_object(py).getattr(py, "source")?;
Ok(GenericRepository::new(source))
})
.unwrap()
}
fn get_target(&self) -> GenericRepository {
Python::attach(|py| -> PyResult<GenericRepository> {
let target = self.to_object(py).getattr(py, "target")?;
Ok(GenericRepository::new(target))
})
.unwrap()
}
// TODO: This should really be on InterGitRepository
fn fetch_refs(
&self,
get_changed_refs: std::sync::Mutex<
Box<
dyn FnMut(
&HashMap<Vec<u8>, (Vec<u8>, Option<RevisionId>)>,
) -> HashMap<Vec<u8>, (Vec<u8>, Option<RevisionId>)>
+ Send,
>,
>,
lossy: bool,
overwrite: bool,
) -> Result<(), Error> {
Python::attach(|py| {
let get_changed_refs =
pyo3::types::PyCFunction::new_closure(py, None, None, move |args, _kwargs| {
let refs = args
.extract::<(HashMap<Vec<u8>, (Vec<u8>, Option<RevisionId>)>,)>()
.unwrap()
.0;
// Call get_changed_refs
let result = if let Ok(mut get_changed_refs) = get_changed_refs.lock() {
get_changed_refs(&refs)
} else {
refs
};
Python::attach(|py| -> PyResult<Py<PyAny>> {
let ret = PyDict::new(py);
for (k, (v, r)) in result {
ret.set_item(
PyBytes::new(py, k.as_slice()),
(
PyBytes::new(py, v.as_slice()),
r.map(|r| r.into_pyobject(py).unwrap().unbind()),
),
)?;
}
// We need to change the return type since pyo3::Python can't be sent between
// threads
Ok(ret.unbind().into())
})
})
.unwrap();
self.to_object(py).call_method1(
py,
"fetch_refs",
(get_changed_refs, lossy, overwrite),
)?;
Ok(())
})
}
}