rust_apt/cache.rs
1//! Contains Cache related structs.
2
3use std::cell::OnceCell;
4use std::fs;
5use std::path::Path;
6
7use cxx::{Exception, UniquePtr};
8
9use crate::config::{init_config_system, Config};
10use crate::depcache::DepCache;
11use crate::error::AptErrors;
12use crate::progress::{AcquireProgress, InstallProgress, OperationProgress};
13use crate::raw::{
14 create_cache, create_pkgmanager, create_problem_resolver, IntoRawIter, IterPkgIterator,
15 PackageManager, PkgCacheFile, PkgIterator, ProblemResolver,
16};
17use crate::records::PackageRecords;
18use crate::util::{apt_lock, apt_unlock, apt_unlock_inner};
19use crate::Package;
20
21/// Selection of Upgrade type
22#[repr(i32)]
23pub enum Upgrade {
24 /// Upgrade will Install new and Remove packages in addition to
25 /// upgrading them.
26 ///
27 /// Equivalent to `apt full-upgrade` and `apt-get dist-upgrade`.
28 FullUpgrade = 0,
29 /// Upgrade will Install new but not Remove packages.
30 ///
31 /// Equivalent to `apt upgrade`.
32 Upgrade = 1,
33 /// Upgrade will Not Install new or Remove packages.
34 ///
35 /// Equivalent to `apt-get upgrade`.
36 SafeUpgrade = 3,
37}
38
39/// Selection of how to sort
40enum Sort {
41 /// Disable the sort method.
42 Disable,
43 /// Enable the sort method.
44 Enable,
45 /// Reverse the sort method.
46 Reverse,
47}
48
49/// Determines how to sort packages from the Cache.
50pub struct PackageSort {
51 names: bool,
52 upgradable: Sort,
53 virtual_pkgs: Sort,
54 installed: Sort,
55 auto_installed: Sort,
56 auto_removable: Sort,
57}
58
59impl Default for PackageSort {
60 fn default() -> PackageSort {
61 PackageSort {
62 names: false,
63 upgradable: Sort::Disable,
64 virtual_pkgs: Sort::Disable,
65 installed: Sort::Disable,
66 auto_installed: Sort::Disable,
67 auto_removable: Sort::Disable,
68 }
69 }
70}
71
72impl PackageSort {
73 /// Packages will be sorted by their names a -> z.
74 pub fn names(mut self) -> Self {
75 self.names = true;
76 self
77 }
78
79 /// Only packages that are upgradable will be included.
80 pub fn upgradable(mut self) -> Self {
81 self.upgradable = Sort::Enable;
82 self
83 }
84
85 /// Only packages that are NOT upgradable will be included.
86 pub fn not_upgradable(mut self) -> Self {
87 self.upgradable = Sort::Reverse;
88 self
89 }
90
91 /// Virtual packages will be included.
92 pub fn include_virtual(mut self) -> Self {
93 self.virtual_pkgs = Sort::Enable;
94 self
95 }
96
97 /// Only Virtual packages will be included.
98 pub fn only_virtual(mut self) -> Self {
99 self.virtual_pkgs = Sort::Reverse;
100 self
101 }
102
103 /// Only packages that are installed will be included.
104 pub fn installed(mut self) -> Self {
105 self.installed = Sort::Enable;
106 self
107 }
108
109 /// Only packages that are NOT installed will be included.
110 pub fn not_installed(mut self) -> Self {
111 self.installed = Sort::Reverse;
112 self
113 }
114
115 /// Only packages that are auto installed will be included.
116 pub fn auto_installed(mut self) -> Self {
117 self.auto_installed = Sort::Enable;
118 self
119 }
120
121 /// Only packages that are manually installed will be included.
122 pub fn manually_installed(mut self) -> Self {
123 self.auto_installed = Sort::Reverse;
124 self
125 }
126
127 /// Only packages that are auto removable will be included.
128 pub fn auto_removable(mut self) -> Self {
129 self.auto_removable = Sort::Enable;
130 self
131 }
132
133 /// Only packages that are NOT auto removable will be included.
134 pub fn not_auto_removable(mut self) -> Self {
135 self.auto_removable = Sort::Reverse;
136 self
137 }
138}
139
140/// The main struct for accessing any and all `apt` data.
141pub struct Cache {
142 pub(crate) ptr: UniquePtr<PkgCacheFile>,
143 depcache: OnceCell<DepCache>,
144 records: OnceCell<PackageRecords>,
145 pkgmanager: OnceCell<UniquePtr<PackageManager>>,
146 problem_resolver: OnceCell<UniquePtr<ProblemResolver>>,
147 local_debs: Vec<String>,
148}
149
150impl Cache {
151 /// Initialize the configuration system, open and return the cache.
152 /// This is the entry point for all operations of this crate.
153 ///
154 /// `local_files` allows you to temporarily add local files to the cache, as
155 /// long as they are one of the following:
156 ///
157 /// - `*.deb` or `*.ddeb` files
158 /// - `Packages` and `Sources` files from apt repositories. These files can
159 /// be compressed.
160 /// - `*.dsc` or `*.changes` files
161 /// - A valid directory containing the file `./debian/control`
162 ///
163 /// This function returns an [`AptErrors`] if any of the files cannot
164 /// be found or are invalid.
165 ///
166 /// Note that if you run [`Cache::commit`] or [`Cache::update`],
167 /// You will be required to make a new cache to perform any further changes
168 pub fn new<T: AsRef<str>>(local_files: &[T]) -> Result<Cache, AptErrors> {
169 let volatile_files: Vec<_> = local_files.iter().map(|d| d.as_ref()).collect();
170
171 init_config_system();
172 Ok(Cache {
173 ptr: create_cache(&volatile_files)?,
174 depcache: OnceCell::new(),
175 records: OnceCell::new(),
176 pkgmanager: OnceCell::new(),
177 problem_resolver: OnceCell::new(),
178 local_debs: volatile_files
179 .into_iter()
180 .filter(|f| f.ends_with(".deb"))
181 .map(|f| f.to_string())
182 .collect(),
183 })
184 }
185
186 /// Internal Method for generating the package list.
187 pub fn raw_pkgs(&self) -> impl Iterator<Item = UniquePtr<PkgIterator>> {
188 unsafe { self.begin().raw_iter() }
189 }
190
191 /// Get the DepCache
192 pub fn depcache(&self) -> &DepCache {
193 self.depcache
194 .get_or_init(|| DepCache::new(unsafe { self.create_depcache() }))
195 }
196
197 /// Get the PkgRecords
198 pub fn records(&self) -> &PackageRecords {
199 self.records
200 .get_or_init(|| PackageRecords::new(unsafe { self.create_records() }))
201 }
202
203 /// Get the PkgManager
204 pub fn pkg_manager(&self) -> &PackageManager {
205 self.pkgmanager
206 .get_or_init(|| unsafe { create_pkgmanager(self.depcache()) })
207 }
208
209 /// Get the ProblemResolver
210 pub fn resolver(&self) -> &ProblemResolver {
211 self.problem_resolver
212 .get_or_init(|| unsafe { create_problem_resolver(self.depcache()) })
213 }
214
215 /// Iterate through the packages in a random order
216 pub fn iter(&self) -> CacheIter {
217 CacheIter {
218 pkgs: unsafe { self.begin().raw_iter() },
219 cache: self,
220 }
221 }
222
223 /// An iterator of packages in the cache.
224 pub fn packages(&self, sort: &PackageSort) -> impl Iterator<Item = Package> {
225 let mut pkg_list = vec![];
226 for pkg in self.raw_pkgs() {
227 match sort.virtual_pkgs {
228 // Virtual packages are enabled, include them.
229 // This works differently than the rest. I should probably change defaults.
230 Sort::Enable => {},
231 // If disabled and pkg has no versions, exclude
232 Sort::Disable => {
233 if unsafe { pkg.versions().end() } {
234 continue;
235 }
236 },
237 // If reverse and the package has versions, exclude
238 // This section is for if you only want virtual packages
239 Sort::Reverse => {
240 if unsafe { !pkg.versions().end() } {
241 continue;
242 }
243 },
244 }
245
246 match sort.upgradable {
247 // Virtual packages are enabled, include them.
248 Sort::Disable => {},
249 // If disabled and pkg has no versions, exclude
250 Sort::Enable => {
251 // If the package isn't installed, then it can not be upgradable
252 if unsafe { pkg.current_version().end() }
253 || !self.depcache().is_upgradable(&pkg)
254 {
255 continue;
256 }
257 },
258 // If reverse and the package is installed and upgradable, exclude
259 // This section is for if you only want packages that are not upgradable
260 Sort::Reverse => {
261 if unsafe { !pkg.current_version().end() }
262 && self.depcache().is_upgradable(&pkg)
263 {
264 continue;
265 }
266 },
267 }
268
269 match sort.installed {
270 // Installed Package is Disabled, so we keep them
271 Sort::Disable => {},
272 Sort::Enable => {
273 if unsafe { pkg.current_version().end() } {
274 continue;
275 }
276 },
277 // Only include installed packages.
278 Sort::Reverse => {
279 if unsafe { !pkg.current_version().end() } {
280 continue;
281 }
282 },
283 }
284
285 match sort.auto_installed {
286 // Installed Package is Disabled, so we keep them
287 Sort::Disable => {},
288 Sort::Enable => {
289 if !self.depcache().is_auto_installed(&pkg) {
290 continue;
291 }
292 },
293 // Only include installed packages.
294 Sort::Reverse => {
295 if self.depcache().is_auto_installed(&pkg) {
296 continue;
297 }
298 },
299 }
300
301 match sort.auto_removable {
302 // auto_removable is Disabled, so we keep them
303 Sort::Disable => {},
304 // If the package is not auto removable skip it.
305 Sort::Enable => {
306 // If the Package isn't auto_removable skip
307 if !self.depcache().is_garbage(&pkg) {
308 continue;
309 }
310 },
311 // If the package is auto removable skip it.
312 Sort::Reverse => {
313 if self.depcache().is_garbage(&pkg) {
314 continue;
315 }
316 },
317 }
318
319 // If this is reached we're clear to include the package.
320 pkg_list.push(pkg);
321 }
322
323 if sort.names {
324 pkg_list.sort_by_cached_key(|pkg| pkg.name().to_string());
325 }
326
327 pkg_list.into_iter().map(|pkg| Package::new(self, pkg))
328 }
329
330 /// Updates the package cache and returns a Result
331 ///
332 /// Here is an example of how you may parse the Error messages.
333 ///
334 /// ```
335 /// use rust_apt::new_cache;
336 /// use rust_apt::progress::AcquireProgress;
337 ///
338 /// let cache = new_cache!().unwrap();
339 /// let mut progress = AcquireProgress::apt();
340
341 /// if let Err(e) = cache.update(&mut progress) {
342 /// for error in e.iter() {
343 /// if error.is_error {
344 /// println!("Error: {}", error.msg);
345 /// } else {
346 /// println!("Warning: {}", error.msg);
347 /// }
348 /// }
349 /// }
350 /// ```
351 /// # Known Errors:
352 /// * E:Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
353 /// * E:Unable to lock directory /var/lib/apt/lists/
354 pub fn update(self, progress: &mut AcquireProgress) -> Result<(), AptErrors> {
355 Ok(self.ptr.update(progress.mut_status())?)
356 }
357
358 /// Mark all packages for upgrade
359 ///
360 /// # Example:
361 ///
362 /// ```
363 /// use rust_apt::new_cache;
364 /// use rust_apt::cache::Upgrade;
365 ///
366 /// let cache = new_cache!().unwrap();
367 ///
368 /// cache.upgrade(Upgrade::FullUpgrade).unwrap();
369 /// ```
370 pub fn upgrade(&self, upgrade_type: Upgrade) -> Result<(), AptErrors> {
371 let mut progress = OperationProgress::quiet();
372 Ok(self
373 .depcache()
374 .upgrade(progress.pin().as_mut(), upgrade_type as i32)?)
375 }
376
377 /// Resolve dependencies with the changes marked on all packages. This marks
378 /// additional packages for installation/removal to satisfy the dependency
379 /// chain.
380 ///
381 /// Note that just running a `mark_*` function on a package doesn't
382 /// guarantee that the selected state will be kept during dependency
383 /// resolution. If you need such, make sure to run
384 /// [`crate::Package::protect`] after marking your requested
385 /// modifications.
386 ///
387 /// If `fix_broken` is set to [`true`], the library will try to repair
388 /// broken dependencies of installed packages.
389 ///
390 /// Returns [`Err`] if there was an error reaching dependency resolution.
391 #[allow(clippy::result_unit_err)]
392 pub fn resolve(&self, fix_broken: bool) -> Result<(), AptErrors> {
393 Ok(self
394 .resolver()
395 .resolve(fix_broken, OperationProgress::quiet().pin().as_mut())?)
396 }
397
398 /// Autoinstall every broken package and run the problem resolver
399 /// Returns false if the problem resolver fails.
400 ///
401 /// # Example:
402 ///
403 /// ```
404 /// use rust_apt::new_cache;
405 ///
406 /// let cache = new_cache!().unwrap();
407 ///
408 /// cache.fix_broken();
409 ///
410 /// for pkg in cache.get_changes(false) {
411 /// println!("Pkg Name: {}", pkg.name())
412 /// }
413 /// ```
414 pub fn fix_broken(&self) -> bool { self.depcache().fix_broken() }
415
416 /// Fetch any archives needed to complete the transaction.
417 ///
418 /// # Returns:
419 /// * A [`Result`] enum: the [`Ok`] variant if fetching succeeded, and
420 /// [`Err`] if there was an issue.
421 ///
422 /// # Example:
423 /// ```
424 /// use rust_apt::new_cache;
425 /// use rust_apt::progress::AcquireProgress;
426 ///
427 /// let cache = new_cache!().unwrap();
428 /// let pkg = cache.get("neovim").unwrap();
429 /// let mut progress = AcquireProgress::apt();
430 ///
431 /// pkg.mark_install(true, true);
432 /// pkg.protect();
433 /// cache.resolve(true).unwrap();
434 ///
435 /// cache.get_archives(&mut progress).unwrap();
436 /// ```
437 /// # Known Errors:
438 /// * W:Problem unlinking the file
439 /// /var/cache/apt/archives/partial/neofetch_7.1.0-4_all.deb -
440 /// PrepareFiles (13: Permission denied)
441 /// * W:Problem unlinking the file
442 /// /var/cache/apt/archives/partial/neofetch_7.1.0-4_all.deb -
443 /// PrepareFiles (13: Permission denied)
444 /// * W:Problem unlinking the file
445 /// /var/cache/apt/archives/partial/neofetch_7.1.0-4_all.deb -
446 /// PrepareFiles (13: Permission denied)
447 /// * W:Problem unlinking the file
448 /// /var/cache/apt/archives/partial/neofetch_7.1.0-4_all.deb -
449 /// PrepareFiles (13: Permission denied)
450 /// * W:Problem unlinking the file
451 /// /var/cache/apt/archives/partial/neofetch_7.1.0-4_all.deb -
452 /// PrepareFiles (13: Permission denied)
453 /// * W:Problem unlinking the file /var/log/apt/eipp.log.xz - FileFd::Open
454 /// (13: Permission denied)
455 /// * W:Could not open file /var/log/apt/eipp.log.xz - open (17: File
456 /// exists)
457 /// * W:Could not open file '/var/log/apt/eipp.log.xz' - EIPP::OrderInstall
458 /// (17: File exists)
459 /// * E:Internal Error, ordering was unable to handle the media swap"
460 pub fn get_archives(&self, progress: &mut AcquireProgress) -> Result<(), Exception> {
461 self.pkg_manager()
462 .get_archives(&self.ptr, self.records(), progress.mut_status())
463 }
464
465 /// Install, remove, and do any other actions requested by the cache.
466 ///
467 /// # Returns:
468 /// * A [`Result`] enum: the [`Ok`] variant if transaction was successful,
469 /// and [`Err`] if there was an issue.
470 ///
471 /// # Example:
472 /// ```
473 /// use rust_apt::new_cache;
474 /// use rust_apt::progress::{AcquireProgress, InstallProgress};
475 ///
476 /// let cache = new_cache!().unwrap();
477 /// let pkg = cache.get("neovim").unwrap();
478 /// let mut acquire_progress = AcquireProgress::apt();
479 /// let mut install_progress = InstallProgress::apt();
480 ///
481 /// pkg.mark_install(true, true);
482 /// pkg.protect();
483 /// cache.resolve(true).unwrap();
484 ///
485 /// // These need root
486 /// // cache.get_archives(&mut acquire_progress).unwrap();
487 /// // cache.do_install(&mut install_progress).unwrap();
488 /// ```
489 ///
490 /// # Known Errors:
491 /// * W:Problem unlinking the file /var/log/apt/eipp.log.xz - FileFd::Open
492 /// (13: Permission denied)
493 /// * W:Could not open file /var/log/apt/eipp.log.xz - open (17: File
494 /// exists)
495 /// * W:Could not open file '/var/log/apt/eipp.log.xz' - EIPP::OrderInstall
496 /// (17: File exists)
497 /// * E:Could not create temporary file for /var/lib/apt/extended_states -
498 /// mkstemp (13: Permission denied)
499 /// * E:Failed to write temporary StateFile /var/lib/apt/extended_states
500 /// * W:Could not open file '/var/log/apt/term.log' - OpenLog (13:
501 /// Permission denied)
502 /// * E:Sub-process /usr/bin/dpkg returned an error code (2)
503 /// * W:Problem unlinking the file /var/cache/apt/pkgcache.bin -
504 /// pkgDPkgPM::Go (13: Permission denied)
505 pub fn do_install(self, progress: &mut InstallProgress) -> Result<(), AptErrors> {
506 Ok(self.pkg_manager().do_install(progress.pin().as_mut())?)
507 }
508
509 /// Handle get_archives and do_install in an easy wrapper.
510 ///
511 /// # Returns:
512 /// * A [`Result`]: the [`Ok`] variant if transaction was successful, and
513 /// [`Err`] if there was an issue.
514 /// # Example:
515 /// ```
516 /// use rust_apt::new_cache;
517 /// use rust_apt::progress::{AcquireProgress, InstallProgress};
518 ///
519 /// let cache = new_cache!().unwrap();
520 /// let pkg = cache.get("neovim").unwrap();
521 /// let mut acquire_progress = AcquireProgress::apt();
522 /// let mut install_progress = InstallProgress::apt();
523 ///
524 /// pkg.mark_install(true, true);
525 /// pkg.protect();
526 /// cache.resolve(true).unwrap();
527 ///
528 /// // This needs root
529 /// // cache.commit(&mut acquire_progress, &mut install_progress).unwrap();
530 /// ```
531 pub fn commit(
532 self,
533 progress: &mut AcquireProgress,
534 install_progress: &mut InstallProgress,
535 ) -> Result<(), AptErrors> {
536 // Lock the whole thing so as to prevent tamper
537 apt_lock()?;
538
539 let config = Config::new();
540 let archive_dir = config.dir("Dir::Cache::Archives", "/var/cache/apt/archives/");
541
542 // Copy local debs into archives dir
543 for deb in &self.local_debs {
544 // If it reaches this point it really will be a valid filename, allegedly
545 if let Some(filename) = Path::new(deb).file_name() {
546 // Append the file name onto the archive dir
547 fs::copy(deb, archive_dir.to_string() + &filename.to_string_lossy())?;
548 }
549 }
550
551 // The archives can be grabbed during the apt lock.
552 self.get_archives(progress)?;
553
554 // If the system is locked we will want to unlock the dpkg files.
555 // This way when dpkg is running it can access its files.
556 apt_unlock_inner();
557
558 // Perform the operation.
559 self.do_install(install_progress)?;
560
561 // Finally Unlock the whole thing.
562 apt_unlock();
563 Ok(())
564 }
565
566 /// Get a single package.
567 ///
568 /// `cache.get("apt")` Returns a Package object for the native arch.
569 ///
570 /// `cache.get("apt:i386")` Returns a Package object for the i386 arch
571 pub fn get(&self, name: &str) -> Option<Package> {
572 Some(Package::new(self, unsafe {
573 self.find_pkg(name).make_safe()?
574 }))
575 }
576
577 /// An iterator over the packages
578 /// that will be altered when `cache.commit()` is called.
579 ///
580 /// # sort_name:
581 /// * [`true`] = Packages will be in alphabetical order
582 /// * [`false`] = Packages will not be sorted by name
583 pub fn get_changes(&self, sort_name: bool) -> impl Iterator<Item = Package> {
584 let mut changed = Vec::new();
585 let depcache = self.depcache();
586
587 for pkg in self.raw_pkgs() {
588 if depcache.marked_install(&pkg)
589 || depcache.marked_delete(&pkg)
590 || depcache.marked_upgrade(&pkg)
591 || depcache.marked_downgrade(&pkg)
592 || depcache.marked_reinstall(&pkg)
593 {
594 changed.push(pkg);
595 }
596 }
597
598 if sort_name {
599 // Sort by cached key seems to be the fastest for what we're doing.
600 // Maybe consider impl ord or something for these.
601 changed.sort_by_cached_key(|pkg| pkg.name().to_string());
602 }
603
604 changed
605 .into_iter()
606 .map(|pkg_ptr| Package::new(self, pkg_ptr))
607 }
608}
609
610/// Iterator Implementation for the Cache.
611pub struct CacheIter<'a> {
612 pkgs: IterPkgIterator,
613 cache: &'a Cache,
614}
615
616impl<'a> Iterator for CacheIter<'a> {
617 type Item = Package<'a>;
618
619 fn next(&mut self) -> Option<Self::Item> { Some(Package::new(self.cache, self.pkgs.next()?)) }
620}
621
622#[cxx::bridge]
623pub(crate) mod raw {
624 impl UniquePtr<PkgRecords> {}
625
626 unsafe extern "C++" {
627 include!("rust-apt/apt-pkg-c/cache.h");
628 type PkgCacheFile;
629
630 type PkgIterator = crate::raw::PkgIterator;
631 type VerIterator = crate::raw::VerIterator;
632 type PkgFileIterator = crate::raw::PkgFileIterator;
633 type PkgRecords = crate::records::raw::PkgRecords;
634 type IndexFile = crate::records::raw::IndexFile;
635 type PkgDepCache = crate::depcache::raw::PkgDepCache;
636 type AcqTextStatus = crate::acquire::raw::AcqTextStatus;
637 type PkgAcquire = crate::acquire::raw::PkgAcquire;
638
639 /// Create the CacheFile.
640 pub fn create_cache(volatile_files: &[&str]) -> Result<UniquePtr<PkgCacheFile>>;
641
642 /// Update the package lists, handle errors and return a Result.
643 pub fn update(self: &PkgCacheFile, progress: Pin<&mut AcqTextStatus>) -> Result<()>;
644
645 /// Loads the index files into PkgAcquire.
646 ///
647 /// Used to get to source list uris.
648 ///
649 /// It's not clear if this returning a bool is useful.
650 pub fn get_indexes(self: &PkgCacheFile, fetcher: &PkgAcquire) -> bool;
651
652 /// Return a pointer to PkgDepcache.
653 ///
654 /// # Safety
655 ///
656 /// The returned UniquePtr cannot outlive the cache.
657 unsafe fn create_depcache(self: &PkgCacheFile) -> UniquePtr<PkgDepCache>;
658
659 /// Return a pointer to PkgRecords.
660 ///
661 /// # Safety
662 ///
663 /// The returned UniquePtr cannot outlive the cache.
664 unsafe fn create_records(self: &PkgCacheFile) -> UniquePtr<PkgRecords>;
665
666 /// The priority of the Version as shown in `apt policy`.
667 pub fn priority(self: &PkgCacheFile, version: &VerIterator) -> i32;
668
669 /// Lookup the IndexFile of the Package file
670 ///
671 /// # Safety
672 ///
673 /// The IndexFile can not outlive PkgCacheFile.
674 ///
675 /// The returned UniquePtr cannot outlive the cache.
676 unsafe fn find_index(self: &PkgCacheFile, file: &PkgFileIterator) -> UniquePtr<IndexFile>;
677
678 /// Return a package by name and optionally architecture.
679 ///
680 /// # Safety
681 ///
682 /// If the Internal Pkg Pointer is NULL, operations can segfault.
683 /// You should call `make_safe()` asap to convert it to an Option.
684 ///
685 /// The returned UniquePtr cannot outlive the cache.
686 unsafe fn find_pkg(self: &PkgCacheFile, name: &str) -> UniquePtr<PkgIterator>;
687
688 /// Return the pointer to the start of the PkgIterator.
689 ///
690 /// # Safety
691 ///
692 /// If the Internal Pkg Pointer is NULL, operations can segfault.
693 /// You should call `raw_iter()` asap.
694 ///
695 /// The returned UniquePtr cannot outlive the cache.
696 unsafe fn begin(self: &PkgCacheFile) -> UniquePtr<PkgIterator>;
697 }
698}