1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
// Back - An extremely simple git issue tracking system. Inspired by tvix's
// panettone
//
// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This file is part of Back.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/agpl.txt>.
use gix::{bstr::ByteSlice, Repository};
use serde::Deserialize;
use serde_json::Value;
use crate::issues::format::BackString;
use super::{Author, Status};
macro_rules! get {
($value:expr, $name:expr, $type_fun:ident) => {
$value
.get($name)
.expect(concat!(
"Expected field ",
stringify!($name),
"to exists, but was missing."
))
.$type_fun()
.expect(concat!(
"Failed to interpret field ",
stringify!($name),
" as ",
stringify!($type),
"!"
))
};
}
#[derive(Deserialize)]
pub(super) struct RawIssue {
pub(super) author: RawAuthor,
#[serde(alias = "ops")]
pub(super) operations: Vec<Operation>,
}
#[derive(Deserialize, Clone, Default, Debug)]
pub(super) struct RawAuthor {
id: String,
}
impl RawAuthor {
pub fn load_identity(&self, repo: &Repository) -> Author {
let commit_obj = repo
.find_reference(&format!("refs/identities/{}", self.id))
.expect("All authors should also have identities")
.peel_to_commit()
.expect("All identities should be commits");
let tree_obj = repo
.find_tree(
commit_obj
.tree()
.expect("The commit should have an tree associated with it")
.id,
)
.expect("This should be a tree");
let data = repo
.find_blob(
tree_obj
.find_entry("version")
.expect("This entry should exist")
.object()
.expect("This should point to a blob entry")
.id,
)
.expect("This blob should exist")
.data
.clone();
let json: Value = serde_json::from_str(data.to_str().expect("This is encoded json"))
.expect("This is valid json");
Author {
name: BackString::from(get! {json, "name", as_str}.to_owned()),
email: BackString::from(get! {json, "email", as_str}.to_owned()),
}
}
}
#[derive(Deserialize)]
#[serde(from = "Value")]
pub(super) enum Operation {
AddComment {
timestamp: u64,
message: String,
// files: Option<String>, TODO
},
SetStatus {
timestamp: u64,
status: Status,
},
Create {
timestamp: u64,
title: String,
message: String,
// files: Option<String>, TODO
},
}
impl From<u64> for Status {
fn from(value: u64) -> Self {
match value {
1 => Status::Open,
2 => Status::Closed,
other => todo!("The status ({other}) is not yet implemented."),
}
}
}
impl From<Value> for Operation {
fn from(value: Value) -> Self {
match value
.get("type")
.expect("Should exist")
.as_u64()
.expect("This should work")
{
1 => Self::Create {
title: get! {value, "title", as_str}.to_owned(),
message: get! {value, "message", as_str}.to_owned(),
timestamp: get! {value, "timestamp", as_u64},
},
3 => Self::AddComment {
message: get! {value, "message", as_str}.to_owned(),
timestamp: get! {value, "timestamp", as_u64},
},
4 => Self::SetStatus {
status: Status::from(get! {value, "status", as_u64}),
timestamp: get! {value, "timestamp", as_u64},
},
other => todo!("The type ({other}) is not yet added as a a valid operation. It's value is: '{value}''"),
}
}
}
|