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