1#![deny(missing_docs)]
20#![allow(unexpected_cfgs)]
23#![allow(clippy::result_large_err)]
25
26pub mod bazaar;
27pub mod branch;
28pub mod clean_tree;
29pub mod commit;
30pub mod config;
31pub mod controldir;
32pub mod cvs;
33pub mod darcs;
34pub mod delta;
35pub mod diff;
36#[cfg(feature = "dirty-tracker")]
37pub mod dirty_tracker;
38pub mod error;
39pub mod export;
40pub mod foreign;
41pub mod forge;
42pub mod fossil;
43pub mod git;
44pub mod github;
45pub mod gitlab;
46pub mod gpg;
47pub mod graph;
48pub mod groupcompress;
50pub mod hooks;
51pub mod interrepository;
52pub mod intertree;
53pub mod knit;
55#[cfg(feature = "launchpad")]
56pub mod launchpad;
57pub mod location;
58pub mod lock;
59pub mod mercurial;
60pub mod merge;
61pub mod osutils;
62pub mod patches;
63pub mod plugin;
64pub mod prelude;
65pub mod rename_map;
66pub mod repository;
67pub mod revisionid;
68pub mod status;
69pub mod subversion;
70pub mod tags;
71pub mod testing;
72pub mod transform;
73pub mod transport;
74pub mod tree;
75pub mod ui;
76pub mod urlutils;
77pub mod version;
78pub mod versionedfiles;
80pub mod weave;
82pub mod workingtree;
83pub mod workspace;
84
85#[cfg(feature = "debian")]
86pub mod debian;
87
88pub use branch::Branch;
91pub use controldir::{ControlDir, Prober};
93pub use forge::{get_forge, Forge, MergeProposal, MergeProposalStatus};
95pub use lock::Lock;
97use pyo3::exceptions::PyImportError;
98use pyo3::prelude::*;
99pub use revisionid::RevisionId;
101pub use transport::{get_transport, Transport};
103pub use tree::{RevisionTree, Tree, WorkingTree};
105pub use urlutils::{join_segment_parameters, split_segment_parameters};
107pub use workspace::reset_tree;
109
110pub fn init_git() {
114 pyo3::Python::attach(|py| {
115 py.import("breezy.git").unwrap();
116 })
117}
118
119pub fn init_bzr() {
123 pyo3::Python::attach(|py| {
124 py.import("breezy.bzr").unwrap();
125 })
126}
127
128#[cfg(feature = "auto-initialize")]
129#[ctor::ctor]
131fn ensure_initialized() {
132 init();
133}
134
135const MINIMUM_VERSION: (usize, usize, usize) = (3, 3, 6);
137
138#[derive(Debug, Clone)]
140pub enum BreezyInitError {
141 NotInstalled,
143 VersionTooOld {
145 installed: (usize, usize, usize),
147 required: (usize, usize, usize),
149 },
150 Other(String),
152}
153
154impl std::fmt::Display for BreezyInitError {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 BreezyInitError::NotInstalled => {
158 write!(f, "Breezy is not installed. Please install Breezy first.")
159 }
160 BreezyInitError::VersionTooOld {
161 installed,
162 required,
163 } => write!(
164 f,
165 "Breezy version {}.{}.{} is too old, please upgrade to at least {}.{}.{}.",
166 installed.0, installed.1, installed.2, required.0, required.1, required.2
167 ),
168 BreezyInitError::Other(e) => write!(f, "{}", e),
169 }
170 }
171}
172
173impl std::error::Error for BreezyInitError {}
174
175static INIT_BREEZY: std::sync::OnceLock<std::result::Result<(), BreezyInitError>> =
177 std::sync::OnceLock::new();
178
179fn do_init() -> std::result::Result<(), BreezyInitError> {
180 pyo3::Python::initialize();
181 let (major, minor, micro) = pyo3::Python::attach(|py| match py.import("breezy") {
182 Ok(breezy) => {
183 let (major, minor, micro, _releaselevel, _serial): (
184 usize,
185 usize,
186 usize,
187 String,
188 usize,
189 ) = breezy.getattr("version_info").unwrap().extract().unwrap();
190 Ok((major, minor, micro))
191 }
192 Err(e) => {
193 if e.is_instance_of::<PyImportError>(py) {
194 Err(BreezyInitError::NotInstalled)
195 } else {
196 Err(BreezyInitError::Other(e.to_string()))
197 }
198 }
199 })?;
200
201 if (major, minor, micro) < MINIMUM_VERSION {
202 return Err(BreezyInitError::VersionTooOld {
203 installed: (major, minor, micro),
204 required: MINIMUM_VERSION,
205 });
206 }
207
208 if major >= 4 {
209 log::warn!("Support for Breezy 4.0 is experimental and incomplete.");
210 }
211
212 init_git();
213 init_bzr();
214
215 pyo3::Python::attach(|py| {
217 let m = py.import("breezy.controldir").unwrap();
218 let f = m.getattr("ControlDirFormat").unwrap();
219 f.call_method0("known_formats").unwrap();
220 });
221
222 pyo3::Python::attach(|py| {
224 let m = py.import("breezy.config").unwrap();
225 m.call_method0("GlobalStack").unwrap();
226 m.call_method1("LocationStack", ("file:///",)).unwrap();
227 });
228
229 Ok(())
230}
231
232pub fn try_init() -> std::result::Result<(), BreezyInitError> {
237 INIT_BREEZY.get_or_init(do_init).clone()
238}
239
240pub fn init() {
250 try_init().unwrap();
251}
252
253pub type Result<R> = std::result::Result<R, crate::error::Error>;