1#![deny(missing_docs)]
16#![allow(unexpected_cfgs)]
19pub mod batch;
20pub mod candidates;
21pub mod checks;
22pub mod codemod;
23#[cfg(feature = "debian")]
24pub mod debian;
25pub mod probers;
26pub mod proposal;
27pub mod publish;
28pub mod recipe;
29pub mod run;
30pub mod utils;
31pub mod vcs;
32pub mod workspace;
33pub use breezyshim::branch::{Branch, GenericBranch};
34pub use breezyshim::controldir::{ControlDir, Prober};
35pub use breezyshim::forge::{Forge, MergeProposal};
36pub use breezyshim::transport::Transport;
37pub use breezyshim::tree::WorkingTree;
38pub use breezyshim::RevisionId;
39use serde::{Deserialize, Deserializer, Serialize, Serializer};
40use std::path::Path;
41
42#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
43pub enum Mode {
45 #[serde(rename = "push")]
46 Push,
48
49 #[serde(rename = "propose")]
50 Propose,
52
53 #[serde(rename = "attempt-push")]
54 #[default]
55 AttemptPush,
57
58 #[serde(rename = "push-derived")]
59 PushDerived,
61
62 #[serde(rename = "bts")]
63 Bts,
65}
66
67impl ToString for Mode {
68 fn to_string(&self) -> String {
69 match self {
70 Mode::Push => "push".to_string(),
71 Mode::Propose => "propose".to_string(),
72 Mode::AttemptPush => "attempt-push".to_string(),
73 Mode::PushDerived => "push-derived".to_string(),
74 Mode::Bts => "bts".to_string(),
75 }
76 }
77}
78
79impl std::str::FromStr for Mode {
80 type Err = String;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 match s {
84 "push" => Ok(Mode::Push),
85 "propose" => Ok(Mode::Propose),
86 "attempt" | "attempt-push" => Ok(Mode::AttemptPush),
87 "push-derived" => Ok(Mode::PushDerived),
88 "bts" => Ok(Mode::Bts),
89 _ => Err(format!("Unknown mode: {}", s)),
90 }
91 }
92}
93
94#[cfg(feature = "pyo3")]
95impl pyo3::FromPyObject<'_> for Mode {
96 fn extract_bound(ob: &pyo3::Bound<pyo3::PyAny>) -> pyo3::PyResult<Self> {
97 use pyo3::prelude::*;
98 let s: std::borrow::Cow<str> = ob.extract()?;
99 match s.as_ref() {
100 "push" => Ok(Mode::Push),
101 "propose" => Ok(Mode::Propose),
102 "attempt-push" => Ok(Mode::AttemptPush),
103 "push-derived" => Ok(Mode::PushDerived),
104 "bts" => Ok(Mode::Bts),
105 _ => Err(pyo3::exceptions::PyValueError::new_err((format!(
106 "Unknown mode: {}",
107 s
108 ),))),
109 }
110 }
111}
112
113#[cfg(feature = "pyo3")]
114impl pyo3::ToPyObject for Mode {
115 fn to_object(&self, py: pyo3::Python) -> pyo3::PyObject {
116 self.to_string().to_object(py)
117 }
118}
119
120pub fn derived_branch_name(script: &str) -> &str {
122 let first_word = script.split(' ').next().unwrap_or("");
123 let script_name = Path::new(first_word).file_stem().unwrap_or_default();
124 script_name.to_str().unwrap_or("")
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
129pub enum CommitPending {
130 #[default]
132 Auto,
133
134 Yes,
136
137 No,
139}
140
141impl std::str::FromStr for CommitPending {
142 type Err = String;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 match s {
146 "auto" => Ok(CommitPending::Auto),
147 "yes" => Ok(CommitPending::Yes),
148 "no" => Ok(CommitPending::No),
149 _ => Err(format!("Unknown commit-pending value: {}", s)),
150 }
151 }
152}
153
154impl Serialize for CommitPending {
155 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
156 where
157 S: Serializer,
158 {
159 match *self {
160 CommitPending::Auto => serializer.serialize_none(),
161 CommitPending::Yes => serializer.serialize_bool(true),
162 CommitPending::No => serializer.serialize_bool(false),
163 }
164 }
165}
166
167impl<'de> Deserialize<'de> for CommitPending {
168 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
169 where
170 D: Deserializer<'de>,
171 {
172 let opt: Option<bool> = Option::deserialize(deserializer)?;
173 Ok(match opt {
174 None => CommitPending::Auto,
175 Some(true) => CommitPending::Yes,
176 Some(false) => CommitPending::No,
177 })
178 }
179}
180
181impl CommitPending {
182 pub fn is_default(&self) -> bool {
184 *self == CommitPending::Auto
185 }
186}
187
188pub trait CodemodResult {
190 fn context(&self) -> serde_json::Value;
192
193 fn value(&self) -> Option<u32>;
195
196 fn target_branch_url(&self) -> Option<url::Url>;
198
199 fn description(&self) -> Option<String>;
201
202 fn tags(&self) -> Vec<(String, Option<RevisionId>)>;
204
205 fn tera_context(&self) -> tera::Context {
207 tera::Context::from_value(self.context()).unwrap()
208 }
209}
210
211pub const VERSION: &str = env!("CARGO_PKG_VERSION");