tackler_core/parser/
tackler_txns.rs1use itertools::Itertools;
6use std::collections::HashSet;
7use std::error::Error;
8use std::path::{Path, PathBuf};
9use std::str;
10use crate::kernel::Settings;
13use crate::kernel::settings::GitInputSelector;
14use crate::model::{TxnData, Txns};
15use crate::parser::tackler_parser;
16use crate::tackler;
17use gix as git;
18use gix::date::time::CustomFormat;
19use gix::hash as gix_hash;
20use gix::objs::tree::EntryKind;
21use tackler_api::metadata::items::{GitInputReference, MetadataItem};
22
23pub 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::try_from(None, txns, settings)
35}
36
37pub fn paths_to_txns(
40 paths: &[PathBuf],
41 settings: &mut Settings,
42) -> Result<TxnData, tackler::Error> {
43 let txns: Result<Txns, tackler::Error> = paths
44 .iter()
45 .map(|p| tackler_parser::txns_file(p, settings))
46 .flatten_ok()
47 .collect();
48
49 TxnData::try_from(None, txns?, settings)
50}
51
52#[allow(clippy::too_many_lines)]
55pub fn git_to_txns(
56 repo_path: &Path,
57 dir: &str,
58 extension: &str,
59 input_selector: GitInputSelector,
60 settings: &mut Settings,
61) -> Result<TxnData, tackler::Error> {
62 let repo = git::open(repo_path)?;
66
67 let (object, reference) = match input_selector {
68 GitInputSelector::CommitId(id) => {
69 let mut candidates = Some(HashSet::default());
70 let prefix = match gix_hash::Prefix::try_from(id.as_str()) {
71 Ok(v) => v,
72 Err(err) => {
73 let msg = format!("Invalid commit id '{id}': {err}");
74 return Err(msg.into());
75 }
76 };
77
78 let res = repo.objects.lookup_prefix(prefix, candidates.as_mut())?;
79 let object_id = match res {
80 Some(Ok(id)) => id,
81 Some(Err(())) => return Err(format!("Ambiguous abbreviated commit id {id}").into()),
82 None => return Err(format!("Unknown commit id '{id}'").into()),
83 };
84 (repo.find_object(object_id)?.try_into_commit()?, None)
86 }
87 GitInputSelector::Reference(ref_str) => {
88 let id_res = repo.rev_parse_single(ref_str.as_bytes());
89 let id = match id_res {
90 Ok(id) => id,
91 Err(err) => {
92 let msg = if let Some(source) = err.source() {
93 format!("{source}")
94 } else {
95 format!("{err}")
96 };
97 return Err(msg.into());
98 }
99 };
100 let reference = if id.to_string().starts_with(ref_str.as_str()) {
101 None
103 } else {
104 Some(ref_str.clone())
105 };
106 (id.object()?.peel_to_commit()?, reference)
108 }
109 };
110
111 let signature = object.author()?;
112 let author = format!("{} <{}>", signature.name, signature.email);
113 let date = signature
114 .time()?
115 .format(CustomFormat::new("%Y-%m-%d %H:%M:%S %z"))?
116 .clone();
117
118 let gitmd = GitInputReference {
119 commit: object.id.to_string(),
120 reference,
121 dir: dir.to_string(),
122 extension: extension.to_string(),
123 subject: object.message()?.summary().to_string(),
124 author,
125 date,
126 };
127
128 let dir = if dir.ends_with('/') {
129 dir.to_string()
130 } else {
131 format!("{dir}/")
132 };
133 let ext = format!(".{extension}");
134
135 let tree = object.tree()?;
136 let txns: Result<Txns, tackler::Error> = tree
140 .traverse()
141 .breadthfirst
142 .files()?
143 .iter()
144 .map(|entry| {
145 use git::objs::tree::EntryKind::{Blob, Link};
146 match EntryKind::from(entry.mode) {
147 Blob => {
148 if entry.filepath.starts_with(str::as_bytes(dir.as_str()))
149 && entry.filepath.ends_with(str::as_bytes(ext.as_str()))
150 {
151 let obj = repo.find_object(entry.oid)?;
152 let par_res =
155 tackler_parser::txns_text(&mut str::from_utf8(&obj.data)?, settings);
156
157 match par_res {
160 Ok(txns) => Ok(txns),
161 Err(err) => {
162 let msg = format!(
163 "\
164 GIT: Error while processing git object\n\
165 \x20 commit id: {}\n\
166 \x20 object id: {}\n\
167 \x20 path: {}\n\
168 \x20 msg: {}\
169 ",
170 object.id, obj.id, entry.filepath, err
171 );
172 Err(msg.into())
173 }
174 }
175 } else {
176 Ok(Vec::default())
178 }
179 }
180 Link => {
181 let obj = repo.find_object(entry.oid)?;
182 let msg = format!(
183 "\
184 GIT: Error while processing git object\n\
185 \x20 commit id: {}\n\
186 \x20 object id: {}\n\
187 \x20 path: {}\n\
188 \x20 msg: {}\
189 ",
190 object.id,
191 obj.id,
192 entry.filepath,
193 "Links inside repository are not supported"
194 );
195 Err(msg.into())
196 }
197 _ => Ok(Vec::default()),
199 }
200 })
201 .flatten_ok()
202 .collect::<Result<Txns, tackler::Error>>();
203
204 TxnData::try_from(
208 Some(MetadataItem::GitInputReference(gitmd)),
209 txns?,
210 settings,
211 )
212}