use crate::branch::{py_tag_selector, Branch, GenericBranch, PyBranch};
use crate::error::Error;
use crate::repository::{GenericRepository, Repository};
use crate::transport::Transport;
use crate::workingtree::GenericWorkingTree;
use crate::location::AsLocation;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
pub trait PyProber: std::any::Any + std::fmt::Debug {
fn to_object(&self, py: Python) -> Py<PyAny>;
}
pub trait Prober: std::fmt::Debug {
fn probe_transport(&self, transport: &Transport) -> Result<bool, Error>;
fn probe(&self, url: &url::Url) -> Result<bool, Error>;
}
impl<T: PyProber> Prober for T {
fn probe_transport(&self, transport: &Transport) -> Result<bool, Error> {
Python::attach(|py| {
let result = self.to_object(py).call_method1(
py,
"probe_transport",
(transport.as_pyobject(),),
)?;
Ok(result.extract(py)?)
})
}
fn probe(&self, url: &url::Url) -> Result<bool, Error> {
Python::attach(|py| {
let result = self
.to_object(py)
.call_method1(py, "probe", (url.to_string(),))?;
Ok(result.extract(py)?)
})
}
}
pub trait PyControlDir: std::any::Any + std::fmt::Debug {
fn to_object(&self, py: Python) -> Py<PyAny>;
}
pub trait ControlDir: std::fmt::Debug {
fn as_any(&self) -> &dyn std::any::Any;
type Branch: Branch + ?Sized;
type Repository: Repository;
type WorkingTree: crate::workingtree::WorkingTree;
fn get_user_url(&self) -> url::Url;
fn get_format(&self) -> ControlDirFormat;
fn user_transport(&self) -> Transport;
fn control_transport(&self) -> Transport;
fn open_repository(&self) -> Result<Self::Repository, Error>;
fn find_repository(&self) -> Result<GenericRepository, Error>;
fn cloning_metadir(&self) -> ControlDirFormat;
fn create_branch(&self, name: Option<&str>) -> Result<Box<Self::Branch>, Error>;
fn create_repository(&self, shared: Option<bool>) -> Result<GenericRepository, Error>;
fn open_branch(&self, branch_name: Option<&str>) -> Result<Box<Self::Branch>, Error>;
fn create_workingtree(&self) -> crate::Result<GenericWorkingTree>;
fn set_branch_reference(&self, branch: &dyn PyBranch, name: Option<&str>) -> crate::Result<()>;
fn push_branch(
&self,
source_branch: &dyn PyBranch,
to_branch_name: Option<&str>,
stop_revision: Option<&crate::RevisionId>,
overwrite: Option<bool>,
tag_selector: Option<Box<dyn Fn(String) -> bool>>,
) -> crate::Result<Box<Self::Branch>>;
fn sprout(
&self,
target: url::Url,
source_branch: Option<&dyn PyBranch>,
create_tree_if_local: Option<bool>,
stacked: Option<bool>,
revision_id: Option<&crate::RevisionId>,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
>;
fn has_workingtree(&self) -> bool;
fn open_workingtree(&self) -> crate::Result<GenericWorkingTree>;
fn branch_names(&self) -> crate::Result<Vec<String>>;
fn has_branch(&self, name: Option<&str>) -> bool;
fn create_branch_and_repo(
&self,
name: Option<&str>,
shared: Option<bool>,
) -> Result<Box<Self::Branch>, Error>;
fn get_branches(&self) -> crate::Result<std::collections::HashMap<String, Box<Self::Branch>>>;
fn list_branches(&self) -> crate::Result<Vec<String>>;
fn find_branches(&self, using: Option<bool>) -> crate::Result<Vec<Box<Self::Branch>>>;
fn get_branch_reference(&self, name: Option<&str>) -> crate::Result<String>;
fn can_convert_format(&self, format: &ControlDirFormat) -> bool;
fn check_conversion_target(&self, target_format: &ControlDirFormat) -> crate::Result<()>;
fn needs_format_conversion(&self, format: Option<&ControlDirFormat>) -> bool;
fn destroy_branch(&self, name: Option<&str>) -> crate::Result<()>;
fn destroy_repository(&self) -> crate::Result<()>;
fn destroy_workingtree(&self) -> crate::Result<()>;
fn destroy_workingtree_metadata(&self) -> crate::Result<()>;
fn get_config(&self) -> crate::Result<crate::config::ConfigStack>;
}
pub struct GenericControlDir(Py<PyAny>);
impl<'py> IntoPyObject<'py> for GenericControlDir {
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 GenericControlDir {
type Error = PyErr;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
Ok(GenericControlDir(obj.to_owned().unbind()))
}
}
impl PyControlDir for GenericControlDir {
fn to_object(&self, py: Python) -> Py<PyAny> {
self.0.clone_ref(py)
}
}
impl GenericControlDir {
pub fn new(obj: Py<PyAny>) -> Self {
Self(obj)
}
}
impl<T: PyControlDir> ControlDir for T {
fn as_any(&self) -> &dyn std::any::Any {
self
}
type Branch = GenericBranch;
type Repository = crate::repository::GenericRepository;
type WorkingTree = crate::workingtree::GenericWorkingTree;
fn get_user_url(&self) -> url::Url {
Python::attach(|py| {
let result = self.to_object(py).getattr(py, "user_url").unwrap();
url::Url::parse(&result.extract::<String>(py).unwrap()).unwrap()
})
}
fn get_format(&self) -> ControlDirFormat {
Python::attach(|py| {
let result = self.to_object(py).getattr(py, "_format")?;
Ok::<_, PyErr>(ControlDirFormat(result))
})
.unwrap()
}
fn user_transport(&self) -> Transport {
Python::attach(|py| {
let result = self.to_object(py).getattr(py, "user_transport").unwrap();
crate::transport::Transport::new(result)
})
}
fn control_transport(&self) -> Transport {
Python::attach(|py| {
let result = self.to_object(py).getattr(py, "control_transport").unwrap();
crate::transport::Transport::new(result)
})
}
fn open_repository(&self) -> Result<GenericRepository, Error> {
Python::attach(|py| {
let result = self.to_object(py).call_method0(py, "open_repository")?;
Ok(GenericRepository::new(result))
})
}
fn find_repository(&self) -> Result<GenericRepository, Error> {
Python::attach(|py| {
let result = self.to_object(py).call_method0(py, "find_repository")?;
Ok(GenericRepository::new(result))
})
}
fn cloning_metadir(&self) -> ControlDirFormat {
Python::attach(|py| {
let result = self.to_object(py).call_method0(py, "cloning_metadir")?;
Ok::<_, PyErr>(ControlDirFormat(result))
})
.unwrap()
}
fn create_branch(&self, name: Option<&str>) -> Result<Box<Self::Branch>, Error> {
Python::attach(|py| {
let branch: Py<PyAny> =
self.to_object(py)
.call_method(py, "create_branch", (name,), None)?;
Ok(Box::new(GenericBranch::from(branch)) as Box<Self::Branch>)
})
}
fn create_repository(&self, shared: Option<bool>) -> Result<GenericRepository, Error> {
Python::attach(|py| {
let kwargs = PyDict::new(py);
if let Some(shared) = shared {
kwargs.set_item("shared", shared)?;
}
let repository =
self.to_object(py)
.call_method(py, "create_repository", (), Some(&kwargs))?;
Ok(GenericRepository::new(repository))
})
}
fn open_branch(&self, branch_name: Option<&str>) -> Result<Box<Self::Branch>, Error> {
Python::attach(|py| {
let branch: Py<PyAny> =
self.to_object(py)
.call_method(py, "open_branch", (branch_name,), None)?;
Ok(Box::new(GenericBranch::from(branch)) as Box<Self::Branch>)
})
}
fn create_workingtree(&self) -> crate::Result<GenericWorkingTree> {
Python::attach(|py| {
let wt = self.to_object(py).call_method0(py, "create_workingtree")?;
Ok(GenericWorkingTree(wt))
})
}
fn set_branch_reference(&self, branch: &dyn PyBranch, name: Option<&str>) -> crate::Result<()> {
Python::attach(|py| {
self.to_object(py).call_method1(
py,
"set_branch_reference",
(&branch.to_object(py), name),
)?;
Ok(())
})
}
fn push_branch(
&self,
source_branch: &dyn PyBranch,
to_branch_name: Option<&str>,
stop_revision: Option<&crate::RevisionId>,
overwrite: Option<bool>,
tag_selector: Option<Box<dyn Fn(String) -> bool>>,
) -> crate::Result<Box<Self::Branch>> {
Python::attach(|py| {
let kwargs = PyDict::new(py);
if let Some(to_branch_name) = to_branch_name {
kwargs.set_item("name", to_branch_name)?;
}
if let Some(tag_selector) = tag_selector {
kwargs.set_item("tag_selector", py_tag_selector(py, tag_selector)?)?;
}
if let Some(overwrite) = overwrite {
kwargs.set_item("overwrite", overwrite)?;
}
if let Some(stop_revision) = stop_revision {
kwargs.set_item("stop_revision", stop_revision.clone())?;
}
let result = self.to_object(py).call_method(
py,
"push_branch",
(&source_branch.to_object(py),),
Some(&kwargs),
)?;
Ok(
Box::new(GenericBranch::from(result.getattr(py, "target_branch")?))
as Box<Self::Branch>,
)
})
}
fn sprout(
&self,
target: url::Url,
source_branch: Option<&dyn PyBranch>,
create_tree_if_local: Option<bool>,
stacked: Option<bool>,
revision_id: Option<&crate::RevisionId>,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let kwargs = PyDict::new(py);
if let Some(create_tree_if_local) = create_tree_if_local {
kwargs
.set_item("create_tree_if_local", create_tree_if_local)
.unwrap();
}
if let Some(stacked) = stacked {
kwargs.set_item("stacked", stacked).unwrap();
}
if let Some(source_branch) = source_branch {
kwargs
.set_item("source_branch", source_branch.to_object(py))
.unwrap();
}
if let Some(revision_id) = revision_id {
kwargs.set_item("revision_id", revision_id.clone()).unwrap();
}
let cd = self.to_object(py).call_method(
py,
"sprout",
(target.to_string(),),
Some(&kwargs),
)?;
Ok(Box::new(GenericControlDir(cd))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
fn has_workingtree(&self) -> bool {
Python::attach(|py| {
let result = self
.to_object(py)
.call_method0(py, "has_workingtree")
.unwrap();
result.extract(py).unwrap()
})
}
fn open_workingtree(&self) -> crate::Result<GenericWorkingTree> {
Python::attach(|py| {
let wt = self.to_object(py).call_method0(py, "open_workingtree")?;
Ok(GenericWorkingTree(wt))
})
}
fn branch_names(&self) -> crate::Result<Vec<String>> {
Python::attach(|py| {
let names = self
.to_object(py)
.call_method0(py, "branch_names")?
.extract::<Vec<String>>(py)?;
Ok(names)
})
}
fn has_branch(&self, name: Option<&str>) -> bool {
Python::attach(|py| {
let result = self
.to_object(py)
.call_method1(py, "has_branch", (name,))
.unwrap();
result.extract(py).unwrap()
})
}
fn create_branch_and_repo(
&self,
name: Option<&str>,
shared: Option<bool>,
) -> Result<Box<Self::Branch>, Error> {
Python::attach(|py| {
let kwargs = PyDict::new(py);
if let Some(shared) = shared {
kwargs.set_item("shared", shared)?;
}
let branch: Py<PyAny> = self.to_object(py).call_method(
py,
"create_branch_and_repo",
(name,),
Some(&kwargs),
)?;
Ok(Box::new(GenericBranch::from(branch)) as Box<Self::Branch>)
})
}
fn get_branches(&self) -> crate::Result<std::collections::HashMap<String, Box<Self::Branch>>> {
Python::attach(|py| {
let branches_dict = self.to_object(py).call_method0(py, "get_branches")?;
let mut branches = std::collections::HashMap::new();
let dict: &Bound<PyDict> = branches_dict
.cast_bound(py)
.map_err(|_| PyErr::new::<pyo3::exceptions::PyTypeError, _>("Expected a dict"))?;
for (key, value) in dict.iter() {
let name: String = key.extract()?;
let branch = GenericBranch::from(value.unbind());
branches.insert(name, Box::new(branch) as Box<Self::Branch>);
}
Ok(branches)
})
}
fn list_branches(&self) -> crate::Result<Vec<String>> {
Python::attach(|py| {
let names = self
.to_object(py)
.call_method0(py, "list_branches")?
.extract::<Vec<String>>(py)?;
Ok(names)
})
}
fn find_branches(&self, using: Option<bool>) -> crate::Result<Vec<Box<Self::Branch>>> {
Python::attach(|py| {
let kwargs = PyDict::new(py);
if let Some(using) = using {
kwargs.set_item("using", using)?;
}
let branches_list =
self.to_object(py)
.call_method(py, "find_branches", (), Some(&kwargs))?;
let mut branches = Vec::new();
let list: &Bound<PyList> = branches_list
.cast_bound(py)
.map_err(|_| PyErr::new::<pyo3::exceptions::PyTypeError, _>("Expected a list"))?;
for item in list.iter() {
let branch = GenericBranch::from(item.unbind());
branches.push(Box::new(branch) as Box<Self::Branch>);
}
Ok(branches)
})
}
fn get_branch_reference(&self, name: Option<&str>) -> crate::Result<String> {
Python::attach(|py| {
let reference = self
.to_object(py)
.call_method1(py, "get_branch_reference", (name,))?
.extract::<String>(py)?;
Ok(reference)
})
}
fn can_convert_format(&self, format: &ControlDirFormat) -> bool {
Python::attach(|py| {
let result = self
.to_object(py)
.call_method1(py, "can_convert_format", (format.0.clone_ref(py),))
.unwrap();
result.extract(py).unwrap()
})
}
fn check_conversion_target(&self, target_format: &ControlDirFormat) -> crate::Result<()> {
Python::attach(|py| {
self.to_object(py).call_method1(
py,
"check_conversion_target",
(target_format.0.clone_ref(py),),
)?;
Ok(())
})
}
fn needs_format_conversion(&self, format: Option<&ControlDirFormat>) -> bool {
Python::attach(|py| {
let result = if let Some(format) = format {
self.to_object(py)
.call_method1(py, "needs_format_conversion", (format.0.clone_ref(py),))
.unwrap()
} else {
self.to_object(py)
.call_method0(py, "needs_format_conversion")
.unwrap()
};
result.extract(py).unwrap()
})
}
fn destroy_branch(&self, name: Option<&str>) -> crate::Result<()> {
Python::attach(|py| {
self.to_object(py)
.call_method1(py, "destroy_branch", (name,))?;
Ok(())
})
}
fn destroy_repository(&self) -> crate::Result<()> {
Python::attach(|py| {
self.to_object(py).call_method0(py, "destroy_repository")?;
Ok(())
})
}
fn destroy_workingtree(&self) -> crate::Result<()> {
Python::attach(|py| {
self.to_object(py).call_method0(py, "destroy_workingtree")?;
Ok(())
})
}
fn destroy_workingtree_metadata(&self) -> crate::Result<()> {
Python::attach(|py| {
self.to_object(py)
.call_method0(py, "destroy_workingtree_metadata")?;
Ok(())
})
}
fn get_config(&self) -> crate::Result<crate::config::ConfigStack> {
Python::attach(|py| {
let config = self.to_object(py).call_method0(py, "get_config")?;
Ok(crate::config::ConfigStack::new(config))
})
}
}
impl std::fmt::Debug for GenericControlDir {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_fmt(format_args!("ControlDir({:?})", self.0))
}
}
pub struct ControlDirFormat(Py<PyAny>);
impl<'py> IntoPyObject<'py> for ControlDirFormat {
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 Clone for ControlDirFormat {
fn clone(&self) -> Self {
Python::attach(|py| ControlDirFormat(self.0.clone_ref(py)))
}
}
impl From<Py<PyAny>> for ControlDirFormat {
fn from(obj: Py<PyAny>) -> Self {
ControlDirFormat(obj)
}
}
impl Default for ControlDirFormat {
fn default() -> Self {
Python::attach(|py| {
let breezy = PyModule::import(py, "breezy.controldir").unwrap();
let cd_format = breezy.getattr("ControlDirFormat").unwrap();
let obj = cd_format.call_method0("get_default_format").unwrap();
assert!(!obj.is_none());
ControlDirFormat(obj.into())
})
}
}
impl ControlDirFormat {
pub fn get_format_string(&self) -> Vec<u8> {
Python::attach(|py| {
self.0
.call_method0(py, "get_format_string")
.unwrap()
.extract(py)
.unwrap()
})
}
pub fn get_format_description(&self) -> String {
Python::attach(|py| {
self.0
.call_method0(py, "get_format_description")
.unwrap()
.extract(py)
.unwrap()
})
}
pub fn is_control_filename(&self, filename: &str) -> bool {
Python::attach(|py| {
self.0
.call_method1(py, "is_control_filename", (filename,))
.unwrap()
.extract(py)
.unwrap()
})
}
pub fn initialize_on_transport(
&self,
transport: &Transport,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let cd =
self.0
.call_method1(py, "initialize_on_transport", (transport.as_pyobject(),))?;
Ok(Box::new(GenericControlDir(cd))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
pub fn initialize(
&self,
location: impl AsLocation,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let cd = self
.0
.call_method1(py, "initialize", (location.as_location(),))?;
Ok(Box::new(GenericControlDir(cd))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
}
pub fn open_tree_or_branch(
location: impl AsLocation,
name: Option<&str>,
possible_transports: Option<&mut Vec<Transport>>,
) -> Result<(Option<GenericWorkingTree>, Box<dyn Branch>), Error> {
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let kwargs = PyDict::new(py);
if let Some(possible_transports) = possible_transports {
kwargs.set_item(
"possible_transports",
possible_transports
.iter()
.map(|t| t.as_pyobject().clone_ref(py))
.collect::<Vec<Py<PyAny>>>(),
)?;
}
let ret = cd.call_method(
"open_tree_or_branch",
(location.as_location(), name),
Some(&kwargs),
)?;
let (tree, branch) = ret.extract::<(Option<Py<PyAny>>, Py<PyAny>)>()?;
let branch = Box::new(GenericBranch::from(branch)) as Box<dyn Branch>;
let tree = tree.map(GenericWorkingTree);
Ok((tree, branch))
})
}
pub fn open(
url: impl AsLocation,
possible_transports: Option<&mut Vec<Transport>>,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let kwargs = PyDict::new(py);
if let Some(possible_transports) = possible_transports {
kwargs.set_item(
"possible_transports",
possible_transports
.iter()
.map(|t| t.as_pyobject().clone_ref(py))
.collect::<Vec<Py<PyAny>>>(),
)?;
}
let controldir = cd.call_method("open", (url.as_location(),), Some(&kwargs))?;
Ok(Box::new(GenericControlDir(controldir.unbind()))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
pub fn create(
url: impl AsLocation,
format: impl AsFormat,
possible_transports: Option<&mut Vec<Transport>>,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let kwargs = PyDict::new(py);
if let Some(format) = format.as_format() {
kwargs.set_item("format", format.clone())?;
}
if let Some(possible_transports) = possible_transports {
kwargs.set_item(
"possible_transports",
possible_transports
.iter()
.map(|t| t.as_pyobject().clone_ref(py))
.collect::<Vec<Py<PyAny>>>(),
)?;
}
let controldir = cd.call_method("create", (url.as_location(),), Some(&kwargs))?;
Ok(Box::new(GenericControlDir(controldir.unbind()))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
pub fn create_on_transport(
transport: &Transport,
format: impl AsFormat,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let format = format.as_format().unwrap().0;
Ok(Box::new(GenericControlDir(format.call_method(
py,
"initialize_on_transport",
(transport.as_pyobject(),),
None,
)?))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
pub fn open_containing_from_transport(
transport: &Transport,
probers: Option<&[&dyn PyProber]>,
) -> Result<
(
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
String,
),
Error,
> {
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let kwargs = PyDict::new(py);
if let Some(probers) = probers {
kwargs.set_item(
"probers",
probers.iter().map(|p| p.to_object(py)).collect::<Vec<_>>(),
)?;
}
let (controldir, subpath): (Py<PyAny>, String) = cd
.call_method(
"open_containing_from_transport",
(transport.as_pyobject(),),
Some(&kwargs),
)?
.extract()?;
Ok((
Box::new(GenericControlDir(controldir))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
subpath,
))
})
}
pub fn open_from_transport(
transport: &Transport,
probers: Option<&[&dyn PyProber]>,
) -> Result<
Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>,
Error,
> {
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let kwargs = PyDict::new(py);
if let Some(probers) = probers {
kwargs.set_item(
"probers",
probers.iter().map(|p| p.to_object(py)).collect::<Vec<_>>(),
)?;
}
let controldir = cd.call_method(
"open_from_transport",
(transport.as_pyobject(),),
Some(&kwargs),
)?;
Ok(Box::new(GenericControlDir(controldir.unbind()))
as Box<
dyn ControlDir<
Branch = GenericBranch,
Repository = GenericRepository,
WorkingTree = crate::workingtree::GenericWorkingTree,
>,
>)
})
}
pub trait AsFormat {
fn as_format(&self) -> Option<ControlDirFormat>;
}
impl AsFormat for &str {
fn as_format(&self) -> Option<ControlDirFormat> {
Python::attach(|py| {
let m = py.import("breezy.controldir").ok()?;
let cd = m.getattr("format_registry").ok()?;
let format = cd
.call_method1("make_controldir", (self.to_string(),))
.ok()?;
Some(ControlDirFormat(format.unbind()))
})
}
}
impl AsFormat for &ControlDirFormat {
fn as_format(&self) -> Option<ControlDirFormat> {
Some(Python::attach(|py| ControlDirFormat(self.0.clone_ref(py))))
}
}
#[deprecated(
since = "0.7.7",
note = "Use `create_branch_convenience_as_generic` instead to avoid unnecessary boxing"
)]
pub fn create_branch_convenience(
base: &url::Url,
force_new_tree: Option<bool>,
format: impl AsFormat,
) -> Result<Box<dyn Branch>, Error> {
create_branch_convenience_as_generic(base, force_new_tree, format)
.map(|b| Box::new(b) as Box<dyn Branch>)
}
pub fn create_branch_convenience_as_generic(
base: &url::Url,
force_new_tree: Option<bool>,
format: impl AsFormat,
) -> Result<GenericBranch, Error> {
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let format = format.as_format();
let kwargs = PyDict::new(py);
if let Some(force_new_tree) = force_new_tree {
kwargs.set_item("force_new_tree", force_new_tree)?;
}
if let Some(format) = format {
kwargs.set_item("format", format.clone())?;
}
let branch = cd.call_method(
"create_branch_convenience",
(base.to_string(),),
Some(&kwargs),
)?;
Ok(GenericBranch::from(branch.unbind()))
})
}
pub fn create_standalone_workingtree(
base: &std::path::Path,
format: impl AsFormat,
) -> Result<GenericWorkingTree, Error> {
let base = base.to_str().unwrap();
Python::attach(|py| {
let m = py.import("breezy.controldir")?;
let cd = m.getattr("ControlDir")?;
let format = format.as_format();
let wt = cd.call_method(
"create_standalone_workingtree",
(base, format.unwrap_or_default()),
None,
)?;
Ok(GenericWorkingTree(wt.unbind()))
})
}
pub struct GenericProber(Py<PyAny>);
impl<'py> IntoPyObject<'py> for GenericProber {
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 GenericProber {
type Error = PyErr;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
Ok(GenericProber(obj.to_owned().unbind()))
}
}
impl PyProber for GenericProber {
fn to_object(&self, py: Python) -> Py<PyAny> {
self.0.clone_ref(py)
}
}
impl GenericProber {
pub fn new(obj: Py<PyAny>) -> Self {
Self(obj)
}
}
impl std::fmt::Debug for GenericProber {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_fmt(format_args!("Prober({:?})", self.0))
}
}
pub fn all_probers() -> Vec<Box<dyn PyProber>> {
Python::attach(|py| -> PyResult<Vec<Box<dyn PyProber>>> {
let m = py.import("breezy.controldir")?;
let cdf = m.getattr("ControlDirFormat")?;
let probers = cdf
.call_method0("all_probers")?
.extract::<Vec<Py<PyAny>>>()?;
Ok(probers
.into_iter()
.map(|p| Box::new(GenericProber::new(p)) as Box<dyn PyProber>)
.collect::<Vec<_>>())
})
.unwrap()
}
pub struct ControlDirFormatRegistry(Py<PyAny>);
impl ControlDirFormatRegistry {
pub fn new() -> Self {
Python::attach(|py| {
let m = py.import("breezy.controldir").unwrap();
let obj = m.getattr("format_registry").unwrap();
ControlDirFormatRegistry(obj.into())
})
}
pub fn make_controldir(&self, format: &str) -> Option<ControlDirFormat> {
Python::attach(
|py| match self.0.call_method1(py, "make_controldir", (format,)) {
Ok(format) => Some(ControlDirFormat(format)),
Err(e) if e.is_instance_of::<pyo3::exceptions::PyKeyError>(py) => None,
Err(e) => panic!("{}", e),
},
)
}
}
impl Default for ControlDirFormatRegistry {
fn default() -> Self {
ControlDirFormatRegistry::new()
}
}
lazy_static::lazy_static! {
pub static ref FORMAT_REGISTRY: ControlDirFormatRegistry = ControlDirFormatRegistry::new();
}
#[cfg(test)]
mod tests {
use super::*;
use crate::workingtree::WorkingTree;
#[test]
fn test_controldir_to_pycontroldir_conversion() {
let tmp_dir = tempfile::tempdir().unwrap();
let wt = create_standalone_workingtree(tmp_dir.path(), "2a").unwrap();
let controldir = wt.controldir();
if let Some(generic_controldir) = controldir.as_any().downcast_ref::<GenericControlDir>() {
let py_controldir: &dyn PyControlDir = generic_controldir;
Python::attach(|py| {
let _obj = py_controldir.to_object(py);
});
} else {
panic!("Failed to downcast ControlDir to GenericControlDir");
}
}
#[test]
fn test_control_dir_format_registry() {
crate::init();
let registry = ControlDirFormatRegistry::new();
let format = registry.make_controldir("2a").unwrap();
let _ = format.get_format_string();
}
#[test]
fn test_format_registry() {
crate::init();
let format = FORMAT_REGISTRY.make_controldir("2a").unwrap();
let _ = format.get_format_string();
}
#[test]
fn test_all_probers() {
crate::init();
let probers = all_probers();
assert!(!probers.is_empty());
}
#[test]
fn test_open_tree_or_branch() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
create_branch_convenience_as_generic(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
&ControlDirFormat::default(),
)
.unwrap();
let (wt, branch) = open_tree_or_branch(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
None,
)
.unwrap();
assert_eq!(
wt.unwrap().basedir().canonicalize().unwrap(),
tmp_dir.path().canonicalize().unwrap()
);
assert_eq!(
branch.get_user_url(),
url::Url::from_directory_path(tmp_dir.path()).unwrap()
);
}
#[test]
fn test_control_dir_format_default() {
crate::init();
let d = ControlDirFormat::default();
d.get_format_string();
}
#[test]
fn test_open() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let e = open(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
)
.unwrap_err();
assert!(matches!(e, Error::NotBranchError(..)),);
let cd = create(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
"2a",
None,
)
.unwrap();
let od = open(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
)
.unwrap();
assert_eq!(
cd.get_format().get_format_string(),
od.get_format().get_format_string()
);
}
#[test]
fn test_create() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let cd = create(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
"2a",
None,
)
.unwrap();
let od = open(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
)
.unwrap();
assert_eq!(
cd.get_format().get_format_string(),
od.get_format().get_format_string()
);
}
#[test]
fn test_create_on_transport() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let transport = crate::transport::get_transport(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
)
.unwrap();
let _cd = create_on_transport(&transport, "2a").unwrap();
}
#[test]
fn test_open_containing_from_transport() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let transport = crate::transport::get_transport(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
)
.unwrap();
let e = open_containing_from_transport(&transport, None).unwrap_err();
assert!(matches!(e, Error::NotBranchError(..)),);
}
#[test]
fn test_open_from_transport() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let transport = crate::transport::get_transport(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
)
.unwrap();
let e = open_from_transport(&transport, None).unwrap_err();
assert!(matches!(e, Error::NotBranchError(..)),);
}
#[test]
fn test_create_standalone_workingtree() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let wt = create_standalone_workingtree(tmp_dir.path(), "2a").unwrap();
assert_eq!(
wt.basedir().canonicalize().unwrap(),
tmp_dir.path().canonicalize().unwrap()
);
}
#[test]
fn test_create_branch_convenience() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let branch = create_branch_convenience_as_generic(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
None,
&ControlDirFormat::default(),
)
.unwrap();
assert_eq!(
branch.get_user_url(),
url::Url::from_directory_path(tmp_dir.path()).unwrap()
);
}
#[test]
fn test_create_repository() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let controldir = create(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
&ControlDirFormat::default(),
None,
)
.unwrap();
let _repo = controldir.create_repository(None).unwrap();
}
#[test]
fn test_create_branch() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let controldir = create(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
&ControlDirFormat::default(),
None,
)
.unwrap();
assert!(matches!(
controldir.create_branch(None),
Err(Error::NoRepositoryPresent)
));
let _repo = controldir.create_repository(None).unwrap();
let _branch = controldir.create_branch(Some("foo")).unwrap();
}
#[test]
fn test_create_workingtree() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let controldir = create(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
&ControlDirFormat::default(),
None,
)
.unwrap();
controldir.create_repository(None).unwrap();
controldir.create_branch(None).unwrap();
let _wt = controldir.create_workingtree().unwrap();
}
#[test]
fn test_branch_names() {
crate::init();
let tmp_dir = tempfile::tempdir().unwrap();
let controldir = create(
&url::Url::from_directory_path(tmp_dir.path()).unwrap(),
&ControlDirFormat::default(),
None,
)
.unwrap();
controldir.create_repository(None).unwrap();
controldir.create_branch(None).unwrap();
assert_eq!(controldir.branch_names().unwrap(), vec!["".to_string()]);
}
}