tackler_core/parser/
tackler_txns.rs1use itertools::Itertools;
6use std::collections::HashSet;
7use std::path::{Path, PathBuf};
8use std::str;
9use crate::kernel::Settings;
12use crate::model::{TxnData, Txns};
13use crate::parser::tackler_parser;
14use crate::tackler;
15use gix as git;
16use gix::hash as gix_hash;
17use gix::objs::tree::EntryKind;
18use tackler_api::metadata::items::{GitInputReference, MetadataItem};
19
20pub enum GitInputSelector {
21 CommitId(String),
22 Reference(String),
23}
24
25pub fn string_to_txns(
26 input: &mut &str,
27 settings: &mut Settings,
28) -> Result<TxnData, tackler::Error> {
29 let txns = tackler_parser::txns_text(input, settings)?;
30
31 TxnData::from(None, txns, &settings.get_hash())
35}
36
37pub fn paths_to_txns(
38 paths: &[PathBuf],
39 settings: &mut Settings,
40) -> Result<TxnData, tackler::Error> {
41 let txns: Result<Txns, tackler::Error> = paths
42 .iter()
43 .map(|p| tackler_parser::txns_file(p, settings))
44 .flatten_ok()
45 .collect();
46
47 TxnData::from(None, txns?, &settings.get_hash())
48}
49
50pub fn git_to_txns(
51 repo_path: &Path,
52 dir: &str,
53 extension: &str,
54 input_selector: GitInputSelector,
55 settings: &mut Settings,
56) -> Result<TxnData, tackler::Error> {
57 let repo = git::open(repo_path)?;
61
62 let (object, reference) = match input_selector {
63 GitInputSelector::CommitId(id) => {
64 let mut candidates = Some(HashSet::default());
65 let prefix = match gix_hash::Prefix::try_from(id.as_str()) {
66 Ok(v) => v,
67 Err(err) => {
68 let msg = format!("Invalid commit id '{id}': {err}");
69 return Err(msg.into());
70 }
71 };
72
73 let res = repo.objects.lookup_prefix(prefix, candidates.as_mut())?;
74 let object_id = match res {
75 Some(Ok(id)) => id,
76 Some(Err(())) => return Err(format!("Ambiguous abbreviated commit id {id}").into()),
77 None => return Err(format!("Unknown commit id '{id}'").into()),
78 };
79 (repo.find_object(object_id)?.try_into_commit()?, None)
81 }
82 GitInputSelector::Reference(ref_str) => {
83 let id = repo.rev_parse_single(ref_str.as_bytes())?;
84 let reference = if id.to_string().starts_with(ref_str.as_str()) {
85 None
87 } else {
88 Some(ref_str.clone())
89 };
90 (id.object()?.peel_to_commit()?, reference)
92 }
93 };
94
95 let gitmd = GitInputReference {
96 commit: object.id.to_string(),
97 reference,
98 dir: dir.to_string(),
99 suffix: extension.to_string(),
100 message: object.message()?.title.to_string(),
101 };
102
103 let tree = object.tree()?;
104 let txns: Result<Txns, tackler::Error> = tree
108 .traverse()
109 .breadthfirst
110 .files()?
111 .iter()
112 .map(|entry| {
113 use git::objs::tree::EntryKind::{Blob, Link};
114 match EntryKind::from(entry.mode) {
115 Blob => {
116 if entry.filepath.starts_with(str::as_bytes(dir))
117 && entry.filepath.ends_with(str::as_bytes(extension))
118 {
119 let obj = repo.find_object(entry.oid)?;
120 let par_res =
123 tackler_parser::txns_text(&mut str::from_utf8(&obj.data)?, settings);
124
125 match par_res {
128 Ok(txns) => Ok(txns),
129 Err(err) => {
130 let msg = format!(
131 "\
132 GIT: Error while processing git object\n\
133 \x20 commit id: {}\n\
134 \x20 object id: {}\n\
135 \x20 path: {}\n\
136 \x20 msg: {}\
137 ",
138 object.id, obj.id, entry.filepath, err
139 );
140 Err(msg.into())
141 }
142 }
143 } else {
144 Ok(Vec::default())
146 }
147 }
148 Link => {
149 let obj = repo.find_object(entry.oid)?;
150 let msg = format!(
151 "\
152 GIT: Error while processing git object\n\
153 \x20 commit id: {}\n\
154 \x20 object id: {}\n\
155 \x20 path: {}\n\
156 \x20 msg: {}\
157 ",
158 object.id,
159 obj.id,
160 entry.filepath,
161 "Links inside repository are not supported"
162 );
163 Err(msg.into())
164 }
165 _ => Ok(Vec::default()),
167 }
168 })
169 .flatten_ok()
170 .collect::<Result<Txns, tackler::Error>>();
171
172 let hash = &settings.get_hash();
176 TxnData::from(Some(MetadataItem::GitInputReference(gitmd)), txns?, hash)
177}