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
//! This crate contains a rust wrapper for the Breezy API, which is written in Python.
//!
//! Breezy itself is being ported to Rust, but until that port has completed, this crate allows
//! access to the most important Breezy APIs via Rust.
//!
//! The Rust API here will follow the Breezy 4.0 Rust API as much as possible, to make porting
//! easier.
//!
//! # Example
//!
//! ```
//! use breezyshim::branch::open as open_branch;
//! breezyshim::plugin::load_plugins();
//! let b = open_branch(&"https://code.launchpad.net/brz".parse().unwrap()).unwrap();
//! println!("Last revision: {:?}", b.last_revision());
//! ```

pub mod bazaar;
pub mod branch;
pub mod config;
pub mod controldir;
pub mod cvs;
pub mod darcs;
pub mod delta;
pub mod diff;
#[cfg(feature = "dirty-tracker")]
pub mod dirty_tracker;
pub mod error;
pub mod export;
pub mod forge;
pub mod fossil;
pub mod git;
pub mod github;
pub mod gitlab;
pub mod gpg;
pub mod graph;
pub mod hooks;
pub mod intertree;
pub mod launchpad;
pub mod location;
pub mod lock;
pub mod mercurial;
pub mod merge;
pub mod osutils;
pub mod patches;
pub mod plugin;
pub mod rename_map;
pub mod repository;
pub mod revisionid;
pub mod status;
pub mod subversion;
pub mod tags;
#[cfg(test)]
pub mod testing;
pub mod transform;
pub mod transport;
pub mod tree;
pub mod urlutils;
pub mod version;
pub mod workspace;

#[cfg(feature = "debian")]
pub mod debian;

pub use branch::Branch;
pub use controldir::{ControlDir, Prober};
pub use forge::{get_forge, Forge, MergeProposal, MergeProposalStatus};
pub use lock::Lock;
use pyo3::exceptions::PyImportError;
use pyo3::prelude::*;
pub use revisionid::RevisionId;
use std::sync::Once;
pub use transport::{get_transport, Transport};
pub use tree::{RevisionTree, Tree, WorkingTree};
pub use urlutils::{join_segment_parameters, split_segment_parameters};
pub use workspace::reset_tree;

pub fn init_git() {
    pyo3::Python::with_gil(|py| {
        py.import_bound("breezy.git").unwrap();
    })
}

pub fn init_bzr() {
    pyo3::Python::with_gil(|py| {
        py.import_bound("breezy.bzr").unwrap();
    })
}

#[cfg(feature = "auto-initialize")]
#[ctor::ctor]
fn ensure_initialized() {
    init();
}

const MINIMUM_VERSION: (usize, usize, usize) = (3, 3, 6);

static INIT_BREEZY: Once = Once::new();

pub fn init() {
    INIT_BREEZY.call_once(|| {
        pyo3::prepare_freethreaded_python();
        let (major, minor, micro) = pyo3::Python::with_gil(|py| match py.import_bound("breezy") {
            Ok(breezy) => {
                let (major, minor, micro, _releaselevel, _serial): (
                    usize,
                    usize,
                    usize,
                    String,
                    usize,
                ) = breezy.getattr("version_info").unwrap().extract().unwrap();
                (major, minor, micro)
            }
            Err(e) => {
                if e.is_instance_of::<PyImportError>(py) {
                    panic!("Breezy is not installed. Please install Breezy first.");
                } else {
                    Err::<(), pyo3::PyErr>(e).unwrap();
                    unreachable!()
                }
            }
        });

        if (major, minor, micro) < MINIMUM_VERSION {
            panic!(
                "Breezy version {} is too old, please upgrade to at least {}.",
                format!("{}.{}.{}", major, minor, micro),
                format!(
                    "{}.{}.{}",
                    MINIMUM_VERSION.0, MINIMUM_VERSION.1, MINIMUM_VERSION.2
                )
            );
        }

        if major >= 4 {
            log::warn!("Support for Breezy 4.0 is experimental and incomplete.");
        }

        init_git();
        init_bzr();

        // Work around a breezy bug
        pyo3::Python::with_gil(|py| {
            let m = py.import_bound("breezy.controldir").unwrap();
            let f = m.getattr("ControlDirFormat").unwrap();
            f.call_method0("known_formats").unwrap();
        });

        // Prevent race conditions
        pyo3::Python::with_gil(|py| {
            let m = py.import_bound("breezy.config").unwrap();
            m.call_method0("GlobalStack").unwrap();
            m.call_method1("LocationStack", ("file:///",)).unwrap();
        });
    });
}

pub type Result<R> = std::result::Result<R, crate::error::Error>;