1use std::ffi::{CStr, CString};
4use std::os::raw::c_char;
5use std::ptr;
6
7extern "C" {
9 fn agc_archive_create_writer() -> *mut std::ffi::c_void;
10 fn agc_archive_open(archive_ptr: *mut std::ffi::c_void, path: *const c_char) -> bool;
11 fn agc_archive_register_stream(archive_ptr: *mut std::ffi::c_void, name: *const c_char) -> i32;
12 fn agc_archive_add_part(
13 archive_ptr: *mut std::ffi::c_void,
14 stream_id: i32,
15 data: *const u8,
16 data_len: usize,
17 metadata: u64,
18 ) -> bool;
19 fn agc_archive_close(archive_ptr: *mut std::ffi::c_void) -> bool;
20 fn agc_archive_destroy(archive_ptr: *mut std::ffi::c_void);
21
22 fn agc_collection_create() -> *mut std::ffi::c_void;
23 fn agc_collection_set_archives(
24 coll_ptr: *mut std::ffi::c_void,
25 out_archive_ptr: *mut std::ffi::c_void,
26 num_threads: u32,
27 batch_size: usize,
28 segment_size: u32,
29 kmer_length: u32,
30 ) -> bool;
31 fn agc_collection_complete_serialization(coll_ptr: *mut std::ffi::c_void);
32 fn agc_collection_store_contig_batch(coll_ptr: *mut std::ffi::c_void, id_from: u32, id_to: u32);
33 fn agc_collection_get_no_samples(coll_ptr: *mut std::ffi::c_void) -> usize;
34 fn agc_collection_destroy(coll_ptr: *mut std::ffi::c_void);
35
36 fn agc_collection_register_sample_contig(
37 coll_ptr: *mut std::ffi::c_void,
38 sample_name: *const c_char,
39 contig_name: *const c_char,
40 ) -> bool;
41
42 fn agc_collection_add_segments_placed(
43 coll_ptr: *mut std::ffi::c_void,
44 placements: *const CSegmentPlacement,
45 count: usize,
46 );
47}
48
49#[repr(C)]
50struct CSegmentPlacement {
51 sample_name: *const c_char,
52 contig_name: *const c_char,
53 seg_part_no: u32,
54 group_id: i32,
55 in_group_id: i32,
56 is_rev_comp: bool,
57 data_size: u32,
58}
59
60pub struct SegmentPlacement {
62 pub sample_name: String,
63 pub contig_name: String,
64 pub seg_part_no: u32,
65 pub group_id: u32,
66 pub in_group_id: u32,
67 pub is_rev_comp: bool,
68 pub data_size: u32,
69}
70
71pub struct CppArchive {
73 ptr: *mut std::ffi::c_void,
74}
75
76impl CppArchive {
77 pub fn new_writer() -> Self {
78 unsafe {
79 let ptr = agc_archive_create_writer();
80 assert!(!ptr.is_null(), "Failed to create C++ archive");
81 Self { ptr }
82 }
83 }
84
85 pub fn open(&mut self, path: &str) -> anyhow::Result<()> {
86 let c_path = CString::new(path)?;
87 unsafe {
88 if agc_archive_open(self.ptr, c_path.as_ptr()) {
89 Ok(())
90 } else {
91 Err(anyhow::anyhow!("Failed to open archive: {}", path))
92 }
93 }
94 }
95
96 pub fn register_stream(&mut self, name: &str) -> anyhow::Result<i32> {
97 let c_name = CString::new(name)?;
98 unsafe {
99 let stream_id = agc_archive_register_stream(self.ptr, c_name.as_ptr());
100 if stream_id >= 0 {
101 Ok(stream_id)
102 } else {
103 Err(anyhow::anyhow!("Failed to register stream: {}", name))
104 }
105 }
106 }
107
108 pub fn add_part(&mut self, stream_id: i32, data: &[u8], metadata: u64) -> anyhow::Result<()> {
109 unsafe {
110 if agc_archive_add_part(self.ptr, stream_id, data.as_ptr(), data.len(), metadata) {
111 Ok(())
112 } else {
113 Err(anyhow::anyhow!(
114 "Failed to add part to stream {}",
115 stream_id
116 ))
117 }
118 }
119 }
120
121 pub fn close(&mut self) -> anyhow::Result<()> {
122 unsafe {
123 if agc_archive_close(self.ptr) {
124 Ok(())
125 } else {
126 Err(anyhow::anyhow!("Failed to close archive"))
127 }
128 }
129 }
130
131 pub fn as_ptr(&mut self) -> *mut std::ffi::c_void {
132 self.ptr
133 }
134}
135
136impl Drop for CppArchive {
137 fn drop(&mut self) {
138 unsafe {
139 agc_archive_destroy(self.ptr);
140 }
141 }
142}
143
144unsafe impl Send for CppArchive {}
146unsafe impl Sync for CppArchive {}
147
148pub struct CppCollection {
150 ptr: *mut std::ffi::c_void,
151}
152
153impl CppCollection {
154 pub fn new() -> Self {
155 unsafe {
156 let ptr = agc_collection_create();
157 assert!(!ptr.is_null(), "Failed to create C++ collection");
158 Self { ptr }
159 }
160 }
161
162 pub fn set_archives(
163 &mut self,
164 archive: &mut CppArchive,
165 num_threads: u32,
166 batch_size: usize,
167 segment_size: u32,
168 kmer_length: u32,
169 ) -> anyhow::Result<()> {
170 unsafe {
171 if agc_collection_set_archives(
172 self.ptr,
173 archive.as_ptr(),
174 num_threads,
175 batch_size,
176 segment_size,
177 kmer_length,
178 ) {
179 Ok(())
180 } else {
181 Err(anyhow::anyhow!("Failed to set archives"))
182 }
183 }
184 }
185
186 pub fn register_sample_contig(
187 &mut self,
188 sample_name: &str,
189 contig_name: &str,
190 ) -> anyhow::Result<()> {
191 let c_sample_name = CString::new(sample_name)?;
192 let c_contig_name = CString::new(contig_name)?;
193
194 unsafe {
195 if agc_collection_register_sample_contig(
196 self.ptr,
197 c_sample_name.as_ptr(),
198 c_contig_name.as_ptr(),
199 ) {
200 Ok(())
201 } else {
202 Err(anyhow::anyhow!(
203 "Failed to register sample '{}' contig '{}'",
204 sample_name,
205 contig_name
206 ))
207 }
208 }
209 }
210
211 pub fn add_segments_placed(&mut self, placements: &[SegmentPlacement]) -> anyhow::Result<()> {
212 if placements.is_empty() {
213 return Ok(());
214 }
215
216 let c_sample_names: Vec<CString> = placements
218 .iter()
219 .map(|p| CString::new(p.sample_name.as_str()).unwrap())
220 .collect();
221
222 let c_contig_names: Vec<CString> = placements
223 .iter()
224 .map(|p| CString::new(p.contig_name.as_str()).unwrap())
225 .collect();
226
227 let c_placements: Vec<CSegmentPlacement> = placements
229 .iter()
230 .enumerate()
231 .map(|(i, p)| CSegmentPlacement {
232 sample_name: c_sample_names[i].as_ptr(),
233 contig_name: c_contig_names[i].as_ptr(),
234 seg_part_no: p.seg_part_no,
235 group_id: p.group_id as i32,
236 in_group_id: p.in_group_id as i32,
237 is_rev_comp: p.is_rev_comp,
238 data_size: p.data_size,
239 })
240 .collect();
241
242 unsafe {
243 agc_collection_add_segments_placed(self.ptr, c_placements.as_ptr(), c_placements.len());
244 }
245
246 Ok(())
247 }
248
249 pub fn complete_serialization(&mut self) {
250 unsafe {
251 agc_collection_complete_serialization(self.ptr);
252 }
253 }
254
255 pub fn store_contig_batch(&mut self, id_from: u32, id_to: u32) {
256 unsafe {
257 agc_collection_store_contig_batch(self.ptr, id_from, id_to);
258 }
259 }
260
261 pub fn get_no_samples(&self) -> usize {
262 unsafe { agc_collection_get_no_samples(self.ptr) }
263 }
264}
265
266impl Drop for CppCollection {
267 fn drop(&mut self) {
268 unsafe {
269 agc_collection_destroy(self.ptr);
270 }
271 }
272}
273
274unsafe impl Send for CppCollection {}
276unsafe impl Sync for CppCollection {}