1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::git::Branch;
6
7#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "kebab-case")]
9pub struct Chapter {
10 pub label: String,
11 pub name: String,
12 pub no_starter: Option<bool>,
13}
14
15impl Chapter {
16 pub fn no_starter(&self) -> bool {
17 self.no_starter.unwrap_or(false)
18 }
19
20 pub fn branch(&self, part: ChapterPart) -> Branch {
21 Branch::new(format!("{}-{}", self.label, part))
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
26pub enum ChapterPart {
27 Starter,
28 Solution,
29}
30
31impl ChapterPart {
32 pub fn next_part(self) -> Option<ChapterPart> {
33 match self {
34 ChapterPart::Starter => Some(ChapterPart::Solution),
35 ChapterPart::Solution => None,
36 }
37 }
38
39 pub fn parse(s: &str) -> Option<ChapterPart> {
40 match s {
41 "a" => Some(ChapterPart::Starter),
42 "b" => Some(ChapterPart::Solution),
43 _ => None,
44 }
45 }
46}
47
48impl fmt::Display for ChapterPart {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 ChapterPart::Starter => write!(f, "a"),
52 ChapterPart::Solution => write!(f, "b"),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
58pub enum ChapterPartStatus {
59 Start,
60 Ongoing,
61}
62
63impl ChapterPartStatus {
64 pub fn is_start(self) -> bool {
65 matches!(self, ChapterPartStatus::Start)
66 }
67
68 pub fn is_ongoing(self) -> bool {
69 matches!(self, ChapterPartStatus::Ongoing)
70 }
71}