use std::str;
use mcport::{Value, json};
use crate::{Commit, EntryKind, Signature, StatusKind, TreeEntry};
pub(super) fn commit_value(commit: &Commit, include_message: bool) -> Value {
json!({
"id": commit.id.to_string(),
"tree": commit.tree.to_string(),
"parents": commit.parents.iter().map(ToString::to_string).collect::<Vec<_>>(),
"author": commit.author.as_ref().map(signature),
"committer": commit.committer.as_ref().map(signature),
"encoding": commit.encoding,
"summary": commit.summary_lossy(),
"message": include_message.then(|| bounded_message(&commit.message))
})
}
pub(super) fn tree_entry(value: &TreeEntry) -> Value {
json!({
"mode": value.mode,
"id": value.id.to_string(),
"kind": entry_kind(value.kind)
})
}
pub(super) fn page_result(
revision: impl Into<String>,
values: Vec<Value>,
cursor: usize,
total: usize,
total_known: bool,
field: &str,
) -> Value {
let next = (cursor + values.len() < total).then_some(cursor + values.len());
let mut result = json!({
"revision": revision.into(),
"cursor": cursor,
"nextCursor": next,
"returned": values.len(),
"scanned": total,
"total": total_known.then_some(total),
"truncated": next.is_some()
});
result[field] = Value::Array(values);
result
}
pub(super) fn path(bytes: &[u8]) -> (String, Option<String>) {
match str::from_utf8(bytes) {
Ok(value) => (value.to_owned(), None),
Err(_) => (
String::from_utf8_lossy(bytes).into_owned(),
Some(hex(bytes)),
),
}
}
pub(super) fn status_kind(value: StatusKind) -> &'static str {
match value {
StatusKind::Unmodified => "unmodified",
StatusKind::Added => "added",
StatusKind::Modified => "modified",
StatusKind::Deleted => "deleted",
StatusKind::TypeChanged => "type-changed",
StatusKind::Unmerged => "unmerged",
}
}
pub(super) fn lower_debug(value: impl std::fmt::Debug) -> String {
format!("{value:?}").to_ascii_lowercase()
}
fn signature(value: &Signature) -> Value {
json!({
"name": value.name,
"email": value.email,
"timestamp": value.timestamp,
"timezoneMinutes": value.timezone_minutes
})
}
pub(super) fn entry_kind(value: EntryKind) -> &'static str {
match value {
EntryKind::Blob => "blob",
EntryKind::Tree => "tree",
EntryKind::Commit => "commit",
}
}
fn bounded_message(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).chars().take(4096).collect()
}
fn hex(bytes: &[u8]) -> String {
const DIGITS: &[u8; 16] = b"0123456789abcdef";
let mut result = String::with_capacity(bytes.len() * 2);
for byte in bytes {
result.push(char::from(DIGITS[usize::from(byte >> 4)]));
result.push(char::from(DIGITS[usize::from(byte & 0x0f)]));
}
result
}