mkit_git_bridge/
headers.rs1use crate::b64;
4use crate::gitobj::{bytes_from_hex, bytes_hex};
5use mkit_core::object::{IDENTITY_MAX_LEN, Identity, IdentityKind};
6
7pub const MKIT_SCHEMA: &str = "mkit-schema";
11pub const MKIT_AUTHOR: &str = "mkit-author";
12pub const MKIT_TAGGER: &str = "mkit-tagger";
13pub const MKIT_SIGNER: &str = "mkit-signer";
14pub const MKIT_SIGNATURE: &str = "mkit-signature";
15pub const MKIT_TREE: &str = "mkit-tree";
16pub const MKIT_PARENT: &str = "mkit-parent";
17pub const MKIT_MESSAGE_HASH: &str = "mkit-message-hash";
18pub const MKIT_CONTENT_DIGEST: &str = "mkit-content-digest";
19pub const MKIT_TARGET: &str = "mkit-target";
20pub const MKIT_TARGET_TYPE: &str = "mkit-target-type";
21
22pub const RESERVED: &[&str] = &["mkit-remix-source", "mkit-object-type"];
26
27pub const SCHEMA_VALUE: &str = "1";
29
30#[must_use]
32pub fn identity_value(identity: &Identity) -> String {
33 format!(
34 "{:02x}:{}",
35 identity.kind as u8,
36 b64::encode(&identity.bytes)
37 )
38}
39
40#[must_use]
42pub fn parse_identity(value: &str) -> Option<Identity> {
43 let (kind_hex, payload_b64) = value.split_once(':')?;
44 let kind_bytes = bytes_from_hex(kind_hex, 1)?;
45 let kind = match kind_bytes[0] {
46 0x01 => IdentityKind::Ed25519,
47 0x02 => IdentityKind::DidKey,
48 0x03 => IdentityKind::Opaque,
49 _ => return None,
50 };
51 let bytes = b64::decode(payload_b64)?;
52 if bytes.is_empty() || bytes.len() > IDENTITY_MAX_LEN as usize {
53 return None;
54 }
55 let id = Identity { kind, bytes };
56 id.is_valid().then_some(id)
57}
58
59#[must_use]
61pub fn hash_value(h: &[u8; 32]) -> String {
62 bytes_hex(h)
63}
64
65#[must_use]
67pub fn parse_hash(value: &str) -> Option<[u8; 32]> {
68 let v = bytes_from_hex(value, 32)?;
69 let mut out = [0u8; 32];
70 out.copy_from_slice(&v);
71 Some(out)
72}
73
74#[must_use]
76pub fn parse_signature(value: &str) -> Option<[u8; 64]> {
77 let v = bytes_from_hex(value, 64)?;
78 let mut out = [0u8; 64];
79 out.copy_from_slice(&v);
80 Some(out)
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn identity_round_trips_all_kinds() {
89 for id in [
90 Identity::ed25519([7; 32]),
91 Identity {
92 kind: IdentityKind::DidKey,
93 bytes: b"z6Mk".to_vec(),
94 },
95 Identity::opaque(vec![0xFF, 0x00, 0x10]),
96 ] {
97 let v = identity_value(&id);
98 assert_eq!(parse_identity(&v).unwrap(), id, "value {v}");
99 }
100 }
101
102 #[test]
103 fn identity_rejects_bad_kind_and_empty() {
104 assert!(parse_identity("04:QQ").is_none());
105 assert!(parse_identity("03:").is_none());
106 assert!(
107 parse_identity("3:QQ").is_none(),
108 "kind must be two hex digits"
109 );
110 }
111
112 #[test]
113 fn hash_value_round_trips() {
114 let h = [0x5A; 32];
115 assert_eq!(parse_hash(&hash_value(&h)).unwrap(), h);
116 assert!(parse_hash("zz").is_none());
117 }
118}