1use std::str;
8
9use derive_builder::Builder;
10use log::warn;
11
12use crate::api::common::NameOrId;
13use crate::api::endpoint_prelude::*;
14use crate::api::projects::repository::files::Encoding;
15use crate::api::ParamValue;
16
17#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
19#[non_exhaustive]
20pub enum CommitActionType {
21 #[default]
23 Create,
24 Delete,
26 Move,
28 Update,
30 Chmod,
32}
33
34impl CommitActionType {
35 pub fn as_str(self) -> &'static str {
37 match self {
38 CommitActionType::Create => "create",
39 CommitActionType::Delete => "delete",
40 CommitActionType::Move => "move",
41 CommitActionType::Update => "update",
42 CommitActionType::Chmod => "chmod",
43 }
44 }
45
46 fn validate(self, builder: &CommitActionBuilder) -> Result<(), CommitActionValidationError> {
47 if builder.content.is_some() {
48 Ok(())
49 } else {
50 match self {
51 Self::Create => Err(CommitActionValidationError::ContentRequiredByCreate),
52 Self::Update => Err(CommitActionValidationError::ContentRequiredByUpdate),
53 _ => Ok(()),
54 }
55 }
56 }
57}
58
59impl ParamValue<'static> for CommitActionType {
60 fn as_value(&self) -> Cow<'static, str> {
61 self.as_str().into()
62 }
63}
64
65const SAFE_ENCODING: Encoding = Encoding::Base64;
66
67#[derive(Debug, Clone, Builder)]
69#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
70pub struct CommitAction<'a> {
71 action: CommitActionType,
73 #[builder(setter(into))]
75 file_path: Cow<'a, str>,
76 #[builder(setter(into), default)]
80 previous_path: Option<Cow<'a, str>>,
81 #[builder(setter(into), default)]
88 content: Option<Cow<'a, [u8]>>,
89 #[builder(default)]
94 encoding: Option<Encoding>,
95 #[builder(setter(into), default)]
99 last_commit_id: Option<Cow<'a, str>>,
100 #[builder(default)]
104 execute_filemode: Option<bool>,
105}
106
107impl<'a> CommitAction<'a> {
108 pub fn builder() -> CommitActionBuilder<'a> {
110 CommitActionBuilder::default()
111 }
112
113 #[allow(clippy::incompatible_msrv)]
116 fn add_query<'b>(&'b self, params: &mut FormParams<'b>) {
117 let (actual_encoding, actual_content) = self
118 .content
119 .as_ref()
120 .map(|content| {
121 let str_content = str::from_utf8(content);
122 let needs_encoding = str_content.is_err();
123 let encoding = self.encoding.unwrap_or_default();
124 let actual_encoding = if needs_encoding && !encoding.is_binary_safe() {
125 warn!(
126 "forcing the encoding to {} due to utf-8 unsafe content",
127 SAFE_ENCODING.as_str(),
128 );
129 SAFE_ENCODING
130 } else {
131 encoding
132 };
133
134 (
135 actual_encoding,
136 actual_encoding.encode(str_content.ok(), content),
137 )
138 })
139 .unzip();
140
141 params
142 .push("actions[][action]", self.action.as_value())
143 .push("actions[][file_path]", self.file_path.as_value())
144 .push_opt("actions[][previous_path]", self.previous_path.as_ref())
145 .push_opt("actions[][content]", actual_content)
146 .push_opt("actions[][encoding]", actual_encoding.or(self.encoding))
147 .push_opt("actions[][last_commit_id]", self.last_commit_id.as_ref())
148 .push_opt("actions[][execute_filemode]", self.execute_filemode);
149 }
150}
151
152static CONTENT_REQUIRED_CREATE: &str = "content is required for create.";
153static CONTENT_REQUIRED_UPDATE: &str = "content is required for update.";
154
155#[non_exhaustive]
156enum CommitActionValidationError {
157 ContentRequiredByCreate,
158 ContentRequiredByUpdate,
159}
160
161impl From<CommitActionValidationError> for CommitActionBuilderError {
162 fn from(validation_error: CommitActionValidationError) -> Self {
163 match validation_error {
164 CommitActionValidationError::ContentRequiredByCreate => {
165 CommitActionBuilderError::ValidationError(CONTENT_REQUIRED_CREATE.into())
166 },
167 CommitActionValidationError::ContentRequiredByUpdate => {
168 CommitActionBuilderError::ValidationError(CONTENT_REQUIRED_UPDATE.into())
169 },
170 }
171 }
172}
173
174impl CommitActionBuilder<'_> {
175 fn validate(&self) -> Result<(), CommitActionValidationError> {
176 if let Some(ref action) = &self.action {
177 action.validate(self)?;
178 }
179
180 Ok(())
181 }
182}
183
184#[derive(Debug, Builder, Clone)]
186#[builder(setter(strip_option), build_fn(validate = "Self::validate"))]
187pub struct CreateCommit<'a> {
188 #[builder(setter(into))]
190 project: NameOrId<'a>,
191 #[builder(setter(into))]
196 branch: Cow<'a, str>,
197 #[builder(setter(into))]
199 commit_message: Cow<'a, str>,
200 #[builder(setter(into), default)]
202 start_branch: Option<Cow<'a, str>>,
203 #[builder(setter(into), default)]
205 start_sha: Option<Cow<'a, str>>,
206 #[builder(setter(into), default)]
210 start_project: Option<NameOrId<'a>>,
211 #[builder(setter(name = "_actions"), private)]
213 actions: Vec<CommitAction<'a>>,
214 #[builder(setter(into), default)]
216 author_email: Option<Cow<'a, str>>,
217 #[builder(setter(into), default)]
219 author_name: Option<Cow<'a, str>>,
220 #[builder(default)]
224 stats: Option<bool>,
225 #[builder(default)]
228 force: Option<bool>,
229}
230
231impl<'a> CreateCommit<'a> {
232 pub fn builder() -> CreateCommitBuilder<'a> {
234 CreateCommitBuilder::default()
235 }
236}
237
238#[non_exhaustive]
239enum CreateCommitValidationError {
240 AtMostOneStartItem,
241}
242
243static AT_MOST_ONE_START_ITEM: &str = "Specify either start_sha or start_branch, not both";
244
245impl From<CreateCommitValidationError> for CreateCommitBuilderError {
246 fn from(validation_error: CreateCommitValidationError) -> Self {
247 match validation_error {
248 CreateCommitValidationError::AtMostOneStartItem => {
249 CreateCommitBuilderError::ValidationError(AT_MOST_ONE_START_ITEM.into())
250 },
251 }
252 }
253}
254
255impl<'a> CreateCommitBuilder<'a> {
256 pub fn action(&mut self, action: CommitAction<'a>) -> &mut Self {
258 self.actions.get_or_insert(Vec::new()).push(action);
259 self
260 }
261
262 pub fn actions<I>(&mut self, iter: I) -> &mut Self
264 where
265 I: IntoIterator<Item = CommitAction<'a>>,
266 {
267 self.actions.get_or_insert(Vec::new()).extend(iter);
268 self
269 }
270
271 fn validate(&self) -> Result<(), CreateCommitValidationError> {
272 let have_start_branch = self
273 .start_branch
274 .as_ref()
275 .map(Option::is_some)
276 .unwrap_or(false);
277 let have_start_sha = self
278 .start_sha
279 .as_ref()
280 .map(Option::is_some)
281 .unwrap_or(false);
282 if have_start_branch && have_start_sha {
283 return Err(CreateCommitValidationError::AtMostOneStartItem);
284 }
285
286 Ok(())
287 }
288}
289
290impl Endpoint for CreateCommit<'_> {
291 fn method(&self) -> Method {
292 Method::POST
293 }
294
295 fn endpoint(&self) -> Cow<'static, str> {
296 format!("projects/{}/repository/commits", self.project).into()
297 }
298
299 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
300 let mut params = FormParams::default();
301
302 params
303 .push("branch", self.branch.as_ref())
304 .push("commit_message", self.commit_message.as_ref())
305 .push_opt("start_branch", self.start_branch.as_ref())
306 .push_opt("start_sha", self.start_sha.as_ref())
307 .push_opt("start_project", self.start_project.as_ref())
308 .push_opt("author_email", self.author_email.as_ref())
309 .push_opt("author_name", self.author_name.as_ref())
310 .push_opt("stats", self.stats)
311 .push_opt("force", self.force);
312
313 for action in self.actions.iter() {
314 action.add_query(&mut params);
315 }
316
317 params.into_body()
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use crate::{
324 api::{self, Query},
325 test::client::{ExpectedUrl, SingleTestClient},
326 };
327
328 use super::*;
329
330 #[test]
331 fn action_action_type_required() {
332 let err = CommitAction::builder()
333 .file_path("path/to/file")
334 .build()
335 .unwrap_err();
336
337 crate::test::assert_missing_field!(err, CommitActionBuilderError, "action");
338 }
339
340 #[test]
341 fn action_file_path_required() {
342 let err = CommitAction::builder()
343 .action(CommitActionType::Create)
344 .content(&b"content"[..])
345 .build()
346 .unwrap_err();
347
348 crate::test::assert_missing_field!(err, CommitActionBuilderError, "file_path");
349 }
350
351 #[test]
352 fn action_content_required_for_create() {
353 let action = CommitAction::builder()
354 .action(CommitActionType::Create)
355 .file_path("path/to/file")
356 .build();
357
358 if let Err(msg) = action {
359 assert_eq!(msg.to_string(), CONTENT_REQUIRED_CREATE)
360 } else {
361 panic!("unexpected error (expected to be missing content)")
362 }
363 }
364
365 #[test]
366 fn action_content_required_for_update() {
367 let action = CommitAction::builder()
368 .action(CommitActionType::Update)
369 .file_path("path/to/file")
370 .build();
371
372 if let Err(msg) = action {
373 assert_eq!(msg.to_string(), CONTENT_REQUIRED_UPDATE)
374 } else {
375 panic!("unexpected error (expected to be missing content)")
376 }
377 }
378
379 #[test]
380 fn project_is_required() {
381 let err = CreateCommit::builder()
382 .branch("source")
383 .commit_message("msg")
384 .action(
385 CommitAction::builder()
386 .action(CommitActionType::Create)
387 .file_path("foo/bar")
388 .content(&b"content"[..])
389 .build()
390 .unwrap(),
391 )
392 .build()
393 .unwrap_err();
394 crate::test::assert_missing_field!(err, CreateCommitBuilderError, "project");
395 }
396
397 #[test]
398 fn branch_is_required() {
399 let err = CreateCommit::builder()
400 .project(1)
401 .commit_message("msg")
402 .action(
403 CommitAction::builder()
404 .action(CommitActionType::Create)
405 .file_path("foo/bar")
406 .content(&b"content"[..])
407 .build()
408 .unwrap(),
409 )
410 .build()
411 .unwrap_err();
412 crate::test::assert_missing_field!(err, CreateCommitBuilderError, "branch");
413 }
414
415 #[test]
416 fn commit_message_is_required() {
417 let err = CreateCommit::builder()
418 .project(1)
419 .branch("source")
420 .action(
421 CommitAction::builder()
422 .action(CommitActionType::Create)
423 .file_path("foo/bar")
424 .content(&b"content"[..])
425 .build()
426 .unwrap(),
427 )
428 .build()
429 .unwrap_err();
430 crate::test::assert_missing_field!(err, CreateCommitBuilderError, "commit_message");
431 }
432
433 #[test]
434 fn actions_required() {
435 let err = CreateCommit::builder()
436 .project(1)
437 .branch("source")
438 .commit_message("msg")
439 .build()
440 .unwrap_err();
441 crate::test::assert_missing_field!(err, CreateCommitBuilderError, "actions");
442 }
443
444 #[test]
445 fn project_branch_msg_and_action_sufficent() {
446 CreateCommit::builder()
447 .project(1)
448 .branch("source")
449 .commit_message("msg")
450 .action(
451 CommitAction::builder()
452 .action(CommitActionType::Create)
453 .file_path("foo/bar")
454 .content(&b"content"[..])
455 .build()
456 .unwrap(),
457 )
458 .build()
459 .unwrap();
460 }
461
462 #[test]
463 fn endpoint() {
464 let endpoint = ExpectedUrl::builder()
465 .method(Method::POST)
466 .endpoint("projects/simple%2Fproject/repository/commits")
467 .content_type("application/x-www-form-urlencoded")
468 .body_str(concat!(
469 "branch=master",
470 "&commit_message=message",
471 "&actions%5B%5D%5Baction%5D=create",
472 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
473 "&actions%5B%5D%5Bcontent%5D=content",
474 "&actions%5B%5D%5Bencoding%5D=text",
475 "&actions%5B%5D%5Baction%5D=delete",
476 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar2",
477 "&actions%5B%5D%5Baction%5D=move",
478 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar3",
479 "&actions%5B%5D%5Bprevious_path%5D=foo%2Fbar4",
480 "&actions%5B%5D%5Bcontent%5D=content",
481 "&actions%5B%5D%5Bencoding%5D=text",
482 "&actions%5B%5D%5Baction%5D=update",
483 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar5",
484 "&actions%5B%5D%5Bcontent%5D=content",
485 "&actions%5B%5D%5Bencoding%5D=text",
486 "&actions%5B%5D%5Baction%5D=chmod",
487 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar5",
488 "&actions%5B%5D%5Bexecute_filemode%5D=true",
489 ))
490 .build()
491 .unwrap();
492 let client = SingleTestClient::new_raw(endpoint, "");
493
494 let endpoint = CreateCommit::builder()
495 .project("simple/project")
496 .branch("master")
497 .commit_message("message")
498 .actions([
499 CommitAction::builder()
500 .action(CommitActionType::Create)
501 .file_path("foo/bar")
502 .content(&b"content"[..])
503 .build()
504 .unwrap(),
505 CommitAction::builder()
506 .action(CommitActionType::Delete)
507 .file_path("foo/bar2")
508 .build()
509 .unwrap(),
510 CommitAction::builder()
511 .action(CommitActionType::Move)
512 .file_path("foo/bar3")
513 .previous_path("foo/bar4")
514 .content(&b"content"[..])
515 .build()
516 .unwrap(),
517 CommitAction::builder()
518 .action(CommitActionType::Update)
519 .file_path("foo/bar5")
520 .content(&b"content"[..])
521 .build()
522 .unwrap(),
523 CommitAction::builder()
524 .action(CommitActionType::Chmod)
525 .file_path("foo/bar5")
526 .execute_filemode(true)
527 .build()
528 .unwrap(),
529 ])
530 .build()
531 .unwrap();
532 api::ignore(endpoint).query(&client).unwrap();
533 }
534
535 #[test]
536 fn endpoint_start_branch() {
537 let endpoint = ExpectedUrl::builder()
538 .method(Method::POST)
539 .endpoint("projects/simple%2Fproject/repository/commits")
540 .content_type("application/x-www-form-urlencoded")
541 .body_str(concat!(
542 "branch=master",
543 "&commit_message=message",
544 "&start_branch=start",
545 "&actions%5B%5D%5Baction%5D=create",
546 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
547 "&actions%5B%5D%5Bcontent%5D=content",
548 "&actions%5B%5D%5Bencoding%5D=text",
549 ))
550 .build()
551 .unwrap();
552 let client = SingleTestClient::new_raw(endpoint, "");
553
554 let endpoint = CreateCommit::builder()
555 .project("simple/project")
556 .branch("master")
557 .commit_message("message")
558 .action(
559 CommitAction::builder()
560 .action(CommitActionType::Create)
561 .file_path("foo/bar")
562 .content(&b"content"[..])
563 .build()
564 .unwrap(),
565 )
566 .start_branch("start")
567 .build()
568 .unwrap();
569 api::ignore(endpoint).query(&client).unwrap();
570 }
571
572 #[test]
573 fn endpoint_start_sha() {
574 let endpoint = ExpectedUrl::builder()
575 .method(Method::POST)
576 .endpoint("projects/simple%2Fproject/repository/commits")
577 .content_type("application/x-www-form-urlencoded")
578 .body_str(concat!(
579 "branch=new-branch",
580 "&commit_message=message",
581 "&start_sha=40b35d15a129e75500bbf3d5db779b6f29376d1a",
582 "&actions%5B%5D%5Baction%5D=create",
583 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
584 "&actions%5B%5D%5Bcontent%5D=content",
585 "&actions%5B%5D%5Bencoding%5D=text",
586 ))
587 .build()
588 .unwrap();
589 let client = SingleTestClient::new_raw(endpoint, "");
590
591 let endpoint = CreateCommit::builder()
592 .project("simple/project")
593 .branch("new-branch")
594 .start_sha("40b35d15a129e75500bbf3d5db779b6f29376d1a")
595 .commit_message("message")
596 .action(
597 CommitAction::builder()
598 .action(CommitActionType::Create)
599 .file_path("foo/bar")
600 .content(&b"content"[..])
601 .build()
602 .unwrap(),
603 )
604 .build()
605 .unwrap();
606 api::ignore(endpoint).query(&client).unwrap();
607 }
608
609 #[test]
610 fn endpoint_start_branch_and_start_sha() {
611 let err = CreateCommit::builder()
612 .project("simple/project")
613 .branch("master")
614 .commit_message("message")
615 .action(
616 CommitAction::builder()
617 .action(CommitActionType::Create)
618 .file_path("foo/bar")
619 .content(&b"content"[..])
620 .build()
621 .unwrap(),
622 )
623 .start_branch("start")
624 .start_sha("start")
625 .build()
626 .unwrap_err();
627
628 assert_eq!(err.to_string(), AT_MOST_ONE_START_ITEM);
629 }
630
631 #[test]
632 fn endpoint_start_project() {
633 let endpoint = ExpectedUrl::builder()
634 .method(Method::POST)
635 .endpoint("projects/simple%2Fproject/repository/commits")
636 .content_type("application/x-www-form-urlencoded")
637 .body_str(concat!(
638 "branch=new-branch",
639 "&commit_message=message",
640 "&start_project=400",
641 "&actions%5B%5D%5Baction%5D=create",
642 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
643 "&actions%5B%5D%5Bcontent%5D=content",
644 "&actions%5B%5D%5Bencoding%5D=text",
645 ))
646 .build()
647 .unwrap();
648 let client = SingleTestClient::new_raw(endpoint, "");
649
650 let endpoint = CreateCommit::builder()
651 .project("simple/project")
652 .branch("new-branch")
653 .start_project(400)
654 .commit_message("message")
655 .action(
656 CommitAction::builder()
657 .action(CommitActionType::Create)
658 .file_path("foo/bar")
659 .content(&b"content"[..])
660 .build()
661 .unwrap(),
662 )
663 .build()
664 .unwrap();
665 api::ignore(endpoint).query(&client).unwrap();
666 }
667
668 #[test]
669 fn endpoint_author_email() {
670 let endpoint = ExpectedUrl::builder()
671 .method(Method::POST)
672 .endpoint("projects/simple%2Fproject/repository/commits")
673 .content_type("application/x-www-form-urlencoded")
674 .body_str(concat!(
675 "branch=master",
676 "&commit_message=message",
677 "&author_email=me%40mail.com",
678 "&actions%5B%5D%5Baction%5D=create",
679 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
680 "&actions%5B%5D%5Bcontent%5D=content",
681 "&actions%5B%5D%5Bencoding%5D=text",
682 ))
683 .build()
684 .unwrap();
685 let client = SingleTestClient::new_raw(endpoint, "");
686
687 let endpoint = CreateCommit::builder()
688 .project("simple/project")
689 .branch("master")
690 .author_email("me@mail.com")
691 .commit_message("message")
692 .action(
693 CommitAction::builder()
694 .action(CommitActionType::Create)
695 .file_path("foo/bar")
696 .content(&b"content"[..])
697 .build()
698 .unwrap(),
699 )
700 .build()
701 .unwrap();
702 api::ignore(endpoint).query(&client).unwrap();
703 }
704
705 #[test]
706 fn endpoint_author_name() {
707 let endpoint = ExpectedUrl::builder()
708 .method(Method::POST)
709 .endpoint("projects/simple%2Fproject/repository/commits")
710 .content_type("application/x-www-form-urlencoded")
711 .body_str(concat!(
712 "branch=master",
713 "&commit_message=message",
714 "&author_name=me",
715 "&actions%5B%5D%5Baction%5D=create",
716 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
717 "&actions%5B%5D%5Bcontent%5D=content",
718 "&actions%5B%5D%5Bencoding%5D=text",
719 ))
720 .build()
721 .unwrap();
722 let client = SingleTestClient::new_raw(endpoint, "");
723
724 let endpoint = CreateCommit::builder()
725 .project("simple/project")
726 .branch("master")
727 .author_name("me")
728 .commit_message("message")
729 .action(
730 CommitAction::builder()
731 .action(CommitActionType::Create)
732 .file_path("foo/bar")
733 .content(&b"content"[..])
734 .build()
735 .unwrap(),
736 )
737 .build()
738 .unwrap();
739 api::ignore(endpoint).query(&client).unwrap();
740 }
741
742 #[test]
743 fn endpoint_stats() {
744 let endpoint = ExpectedUrl::builder()
745 .method(Method::POST)
746 .endpoint("projects/simple%2Fproject/repository/commits")
747 .content_type("application/x-www-form-urlencoded")
748 .body_str(concat!(
749 "branch=master",
750 "&commit_message=message",
751 "&stats=true",
752 "&actions%5B%5D%5Baction%5D=create",
753 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
754 "&actions%5B%5D%5Bcontent%5D=content",
755 "&actions%5B%5D%5Bencoding%5D=text",
756 ))
757 .build()
758 .unwrap();
759 let client = SingleTestClient::new_raw(endpoint, "");
760
761 let endpoint = CreateCommit::builder()
762 .project("simple/project")
763 .branch("master")
764 .stats(true)
765 .commit_message("message")
766 .action(
767 CommitAction::builder()
768 .action(CommitActionType::Create)
769 .file_path("foo/bar")
770 .content(&b"content"[..])
771 .build()
772 .unwrap(),
773 )
774 .build()
775 .unwrap();
776 api::ignore(endpoint).query(&client).unwrap();
777 }
778
779 #[test]
780 fn endpoint_force() {
781 let endpoint = ExpectedUrl::builder()
782 .method(Method::POST)
783 .endpoint("projects/simple%2Fproject/repository/commits")
784 .content_type("application/x-www-form-urlencoded")
785 .body_str(concat!(
786 "branch=master",
787 "&commit_message=message",
788 "&force=true",
789 "&actions%5B%5D%5Baction%5D=create",
790 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
791 "&actions%5B%5D%5Bcontent%5D=content",
792 "&actions%5B%5D%5Bencoding%5D=text",
793 ))
794 .build()
795 .unwrap();
796 let client = SingleTestClient::new_raw(endpoint, "");
797
798 let endpoint = CreateCommit::builder()
799 .project("simple/project")
800 .branch("master")
801 .force(true)
802 .commit_message("message")
803 .action(
804 CommitAction::builder()
805 .action(CommitActionType::Create)
806 .file_path("foo/bar")
807 .content(&b"content"[..])
808 .build()
809 .unwrap(),
810 )
811 .build()
812 .unwrap();
813 api::ignore(endpoint).query(&client).unwrap();
814 }
815
816 #[test]
817 fn endpoint_encoding() {
818 let endpoint = ExpectedUrl::builder()
819 .method(Method::POST)
820 .endpoint("projects/simple%2Fproject/repository/commits")
821 .content_type("application/x-www-form-urlencoded")
822 .body_str(concat!(
823 "branch=master",
824 "&commit_message=message",
825 "&actions%5B%5D%5Baction%5D=create",
826 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
827 "&actions%5B%5D%5Bcontent%5D=Y29udGVudA%3D%3D",
828 "&actions%5B%5D%5Bencoding%5D=base64",
829 ))
830 .build()
831 .unwrap();
832 let client = SingleTestClient::new_raw(endpoint, "");
833
834 let endpoint = CreateCommit::builder()
835 .project("simple/project")
836 .branch("master")
837 .commit_message("message")
838 .action(
839 CommitAction::builder()
840 .action(CommitActionType::Create)
841 .file_path("foo/bar")
842 .encoding(Encoding::Base64)
843 .content(&b"content"[..])
844 .build()
845 .unwrap(),
846 )
847 .build()
848 .unwrap();
849 api::ignore(endpoint).query(&client).unwrap();
850 }
851
852 #[test]
853 fn endpoint_encoding_fallback() {
854 let endpoint = ExpectedUrl::builder()
855 .method(Method::POST)
856 .endpoint("projects/simple%2Fproject/repository/commits")
857 .content_type("application/x-www-form-urlencoded")
858 .body_str(concat!(
859 "branch=master",
860 "&commit_message=message",
861 "&actions%5B%5D%5Baction%5D=create",
862 "&actions%5B%5D%5Bfile_path%5D=foo%2Fbar",
863 "&actions%5B%5D%5Bcontent%5D=Y29udGVudP8%3D",
864 "&actions%5B%5D%5Bencoding%5D=base64",
865 ))
866 .build()
867 .unwrap();
868 let client = SingleTestClient::new_raw(endpoint, "");
869
870 let endpoint = CreateCommit::builder()
871 .project("simple/project")
872 .branch("master")
873 .commit_message("message")
874 .action(
875 CommitAction::builder()
876 .action(CommitActionType::Create)
877 .file_path("foo/bar")
878 .encoding(Encoding::Text)
879 .content(&b"content\xff"[..])
880 .build()
881 .unwrap(),
882 )
883 .build()
884 .unwrap();
885 api::ignore(endpoint).query(&client).unwrap();
886 }
887}