1use derive_builder::Builder;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5use crate::editor::{
6 utils::{add_if_present, from_element_using_builder, sync_element},
7 ElementConverter, HasElementName, UpdatableElement,
8};
9#[derive(Debug, Clone, Copy, Error)]
10pub enum SCMError {
11 #[error("The scm did not start with scm")]
12 DidNotStartWithScm,
13 #[error("The scm did not have a provider")]
14 MissingProvider,
15}
16
17#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
18#[serde(rename_all = "camelCase")]
19pub struct Scm {
20 #[builder(setter(into, strip_option), default)]
22 pub url: Option<String>,
23 #[builder(setter(into, strip_option), default)]
24 pub connection: Option<String>,
25 #[builder(setter(into, strip_option), default)]
26 pub tag: Option<String>,
27 #[builder(setter(into, strip_option), default)]
28 pub developer_connection: Option<String>,
29}
30impl Scm {
31 pub fn get_provider_for_connection(&self) -> Result<Option<String>, SCMError> {
38 let Some((_, provider, _)) = self.split_connection()? else {
39 return Ok(None);
40 };
41 Ok(Some(provider.to_owned()))
42 }
43 pub fn get_provider_specific_for_connection(&self) -> Result<Option<String>, SCMError> {
53 let Some((_, _, url)) = self.split_connection()? else {
54 return Ok(None);
55 };
56 Ok(Some(url.join(":")))
57 }
58 #[allow(clippy::type_complexity)]
59 fn split_connection(&self) -> Result<Option<(&str, &str, Vec<&str>)>, SCMError> {
60 let Some(url) = self.connection.as_deref() else {
61 return Err(SCMError::MissingProvider);
62 };
63 let mut parts = url.split(':');
64 let part_one = parts.next().ok_or(SCMError::DidNotStartWithScm)?;
65 if part_one != "scm" {
66 return Err(SCMError::DidNotStartWithScm);
67 }
68 let part_two = parts.next().ok_or(SCMError::MissingProvider)?;
69 let part_three = parts.collect::<Vec<&str>>();
70 Ok(Some((part_one, part_two, part_three)))
71 }
72}
73
74impl UpdatableElement for Scm {
75 fn update_element(
76 &self,
77 element: edit_xml::Element,
78 document: &mut edit_xml::Document,
79 ) -> Result<(), crate::editor::XMLEditorError> {
80 let Self {
81 url,
82 connection,
83 tag,
84 developer_connection,
85 } = self;
86 sync_element(document, element, "url", url.as_deref());
87 sync_element(document, element, "connection", connection.as_deref());
88 sync_element(document, element, "tag", tag.as_deref());
89 sync_element(
90 document,
91 element,
92 "developerConnection",
93 developer_connection.as_deref(),
94 );
95 Ok(())
96 }
97}
98impl HasElementName for Scm {
99 fn element_name() -> &'static str {
100 "scm"
101 }
102}
103impl ElementConverter for Scm {
104 from_element_using_builder!(
105 ScmBuilder,
106 element,
107 document,
108 "url" => url,
109 "connection" => connection,
110 "tag" => tag,
111 "developerConnection" => developer_connection
112 );
113
114 fn into_children(
115 self,
116 document: &mut edit_xml::Document,
117 ) -> Result<Vec<edit_xml::Element>, crate::editor::XMLEditorError> {
118 let Self {
119 url,
120 connection,
121 tag,
122 developer_connection,
123 } = self;
124 let mut children = vec![];
125 add_if_present!(document, children, url, "url");
126 add_if_present!(document, children, connection, "connection");
127 add_if_present!(document, children, tag, "tag");
128 add_if_present!(
129 document,
130 children,
131 developer_connection,
132 "developerConnection"
133 );
134 Ok(children)
135 }
136}