Move git related things into specialized git crate

Julia and Mikayla Maki created

Co-Authored-By: Mikayla Maki <mikayla@zed.dev>

Change summary

Cargo.lock                        | 22 ++++++++++++-
crates/editor/Cargo.toml          |  1 
crates/editor/src/element.rs      |  2 
crates/editor/src/multi_buffer.rs |  8 ++--
crates/git/Cargo.toml             | 22 +++++++++++++
crates/git/src/diff.rs            |  6 ---
crates/git/src/git.rs             | 12 +++++++
crates/git/src/repository.rs      | 20 ++++-------
crates/language/Cargo.toml        |  2 
crates/language/src/buffer.rs     | 33 +++++++++----------
crates/language/src/language.rs   |  1 
crates/project/Cargo.toml         |  3 -
crates/project/src/fs.rs          |  4 --
crates/project/src/project.rs     | 43 ++++++++++++++++---------
crates/project/src/worktree.rs    | 54 ++++++++++++++++++--------------
crates/util/src/lib.rs            |  7 ----
crates/util/src/test.rs           |  9 +++--
17 files changed, 150 insertions(+), 99 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1697,6 +1697,7 @@ dependencies = [
  "env_logger",
  "futures",
  "fuzzy",
+ "git",
  "gpui",
  "indoc",
  "itertools",
@@ -2224,6 +2225,23 @@ dependencies = [
  "stable_deref_trait",
 ]
 
+[[package]]
+name = "git"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clock",
+ "git2",
+ "lazy_static",
+ "log",
+ "parking_lot 0.11.2",
+ "smol",
+ "sum_tree",
+ "text",
+ "unindent",
+ "util",
+]
+
 [[package]]
 name = "git2"
 version = "0.15.0"
@@ -2853,7 +2871,7 @@ dependencies = [
  "env_logger",
  "futures",
  "fuzzy",
- "git2",
+ "git",
  "gpui",
  "lazy_static",
  "log",
@@ -3996,7 +4014,7 @@ dependencies = [
  "fsevent",
  "futures",
  "fuzzy",
- "git2",
+ "git",
  "gpui",
  "ignore",
  "language",

crates/editor/Cargo.toml 🔗

@@ -25,6 +25,7 @@ clock = { path = "../clock" }
 collections = { path = "../collections" }
 context_menu = { path = "../context_menu" }
 fuzzy = { path = "../fuzzy" }
+git = { path = "../git" }
 gpui = { path = "../gpui" }
 language = { path = "../language" }
 lsp = { path = "../lsp" }

crates/editor/src/element.rs 🔗

@@ -16,6 +16,7 @@ use crate::{
 };
 use clock::ReplicaId;
 use collections::{BTreeMap, HashMap};
+use git::diff::{DiffHunk, DiffHunkStatus};
 use gpui::{
     color::Color,
     elements::*,
@@ -34,7 +35,6 @@ use gpui::{
     WeakViewHandle,
 };
 use json::json;
-use language::git::{DiffHunk, DiffHunkStatus};
 use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection};
 use project::ProjectPath;
 use settings::Settings;

crates/editor/src/multi_buffer.rs 🔗

@@ -4,13 +4,13 @@ pub use anchor::{Anchor, AnchorRangeExt};
 use anyhow::Result;
 use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet};
+use git::diff::DiffHunk;
 use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
 pub use language::Completion;
 use language::{
-    char_kind, git::DiffHunk, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind,
-    Chunk, DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline,
-    OutlineItem, Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _,
-    TransactionId,
+    char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk,
+    DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem,
+    Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId,
 };
 use smallvec::SmallVec;
 use std::{

crates/git/Cargo.toml 🔗

@@ -0,0 +1,22 @@
+[package]
+name = "git"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "src/git.rs"
+
+[dependencies]
+anyhow = "1.0.38"
+clock = { path = "../clock" }
+git2 = { version = "0.15", default-features = false }
+lazy_static = "1.4.0"
+sum_tree = { path = "../sum_tree" }
+text = { path = "../text" }
+util = { path = "../util" }
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
+smol = "1.2"
+parking_lot = "0.11.1"
+
+[dev-dependencies]
+unindent = "0.1.7"

crates/language/src/git.rs → crates/git/src/diff.rs 🔗

@@ -259,7 +259,7 @@ mod tests {
     use text::Buffer;
     use unindent::Unindent as _;
 
-    #[gpui::test]
+    #[test]
     fn test_buffer_diff_simple() {
         let head_text = "
             one
@@ -308,8 +308,4 @@ mod tests {
             );
         }
     }
-
-    // use rand::rngs::StdRng;
-    // #[gpui::test(iterations = 100)]
-    // fn test_buffer_diff_random(mut rng: StdRng) {}
 }

crates/git/src/git.rs 🔗

@@ -0,0 +1,12 @@
+use std::ffi::OsStr;
+
+pub use git2 as libgit;
+pub use lazy_static::lazy_static;
+
+pub mod diff;
+pub mod repository;
+
+lazy_static! {
+    pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git");
+    pub static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
+}

crates/project/src/git_repository.rs → crates/git/src/repository.rs 🔗

@@ -1,7 +1,7 @@
 use anyhow::Result;
-use git2::{Repository as LibGitRepository, RepositoryOpenFlags as LibGitRepositoryOpenFlags};
+use git2::Repository as LibGitRepository;
 use parking_lot::Mutex;
-use std::{ffi::OsStr, path::Path, sync::Arc};
+use std::{path::Path, sync::Arc};
 use util::ResultExt;
 
 #[derive(Clone)]
@@ -53,21 +53,17 @@ impl GitRepository {
         self.scan_id
     }
 
-    pub(super) fn set_scan_id(&mut self, scan_id: usize) {
+    pub fn set_scan_id(&mut self, scan_id: usize) {
+        println!("setting scan id");
         self.scan_id = scan_id;
     }
 
-    pub fn with_repo<F: FnOnce(&mut git2::Repository)>(&mut self, f: F) {
-        let mut git2 = self.libgit_repository.lock();
-        f(&mut git2)
-    }
-
-    pub async fn load_head_text(&self, file_path: &Path) -> Option<String> {
-        fn logic(repo: &LibGitRepository, file_path: &Path) -> Result<Option<String>> {
+    pub async fn load_head_text(&self, relative_file_path: &Path) -> Option<String> {
+        fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result<Option<String>> {
             let object = repo
                 .head()?
                 .peel_to_tree()?
-                .get_path(file_path)?
+                .get_path(relative_file_path)?
                 .to_object(&repo)?;
 
             let content = match object.as_blob() {
@@ -79,7 +75,7 @@ impl GitRepository {
             Ok(Some(head_text))
         }
 
-        match logic(&self.libgit_repository.lock(), file_path) {
+        match logic(&self.libgit_repository.as_ref().lock(), relative_file_path) {
             Ok(value) => return value,
             Err(err) => log::error!("Error loading head text: {:?}", err),
         }

crates/language/Cargo.toml 🔗

@@ -25,6 +25,7 @@ client = { path = "../client" }
 clock = { path = "../clock" }
 collections = { path = "../collections" }
 fuzzy = { path = "../fuzzy" }
+git = { path = "../git" }
 gpui = { path = "../gpui" }
 lsp = { path = "../lsp" }
 rpc = { path = "../rpc" }
@@ -51,7 +52,6 @@ smol = "1.2"
 tree-sitter = "0.20"
 tree-sitter-rust = { version = "*", optional = true }
 tree-sitter-typescript = { version = "*", optional = true }
-git2 = { version = "0.15", default-features = false }
 
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }

crates/language/src/buffer.rs 🔗

@@ -1,4 +1,3 @@
-use crate::git;
 pub use crate::{
     diagnostic_set::DiagnosticSet,
     highlight_map::{HighlightId, HighlightMap},
@@ -47,14 +46,14 @@ pub use {tree_sitter_rust, tree_sitter_typescript};
 pub use lsp::DiagnosticSeverity;
 
 struct GitDiffStatus {
-    diff: git::BufferDiff,
+    diff: git::diff::BufferDiff,
     update_in_progress: bool,
     update_requested: bool,
 }
 
 pub struct Buffer {
     text: TextBuffer,
-    head_text: Option<Arc<String>>,
+    head_text: Option<String>,
     git_diff_status: GitDiffStatus,
     file: Option<Arc<dyn File>>,
     saved_version: clock::Global,
@@ -83,7 +82,7 @@ pub struct Buffer {
 
 pub struct BufferSnapshot {
     text: text::BufferSnapshot,
-    pub git_diff: git::BufferDiff,
+    pub git_diff: git::diff::BufferDiff,
     pub(crate) syntax: SyntaxSnapshot,
     file: Option<Arc<dyn File>>,
     diagnostics: DiagnosticSet,
@@ -353,7 +352,7 @@ impl Buffer {
     ) -> Self {
         Self::build(
             TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()),
-            head_text.map(|h| Arc::new(h.into())),
+            head_text.map(|h| h.into().into_boxed_str().into()),
             Some(file),
         )
     }
@@ -364,7 +363,11 @@ impl Buffer {
         file: Option<Arc<dyn File>>,
     ) -> Result<Self> {
         let buffer = TextBuffer::new(replica_id, message.id, message.base_text);
-        let mut this = Self::build(buffer, message.head_text.map(|text| Arc::new(text)), file);
+        let mut this = Self::build(
+            buffer,
+            message.head_text.map(|text| text.into_boxed_str().into()),
+            file,
+        );
         this.text.set_line_ending(proto::deserialize_line_ending(
             proto::LineEnding::from_i32(message.line_ending)
                 .ok_or_else(|| anyhow!("missing line_ending"))?,
@@ -420,11 +423,7 @@ impl Buffer {
         self
     }
 
-    fn build(
-        buffer: TextBuffer,
-        head_text: Option<Arc<String>>,
-        file: Option<Arc<dyn File>>,
-    ) -> Self {
+    fn build(buffer: TextBuffer, head_text: Option<String>, file: Option<Arc<dyn File>>) -> Self {
         let saved_mtime = if let Some(file) = file.as_ref() {
             file.mtime()
         } else {
@@ -440,7 +439,7 @@ impl Buffer {
             text: buffer,
             head_text,
             git_diff_status: GitDiffStatus {
-                diff: git::BufferDiff::new(),
+                diff: git::diff::BufferDiff::new(),
                 update_in_progress: false,
                 update_requested: false,
             },
@@ -613,7 +612,7 @@ impl Buffer {
                 cx,
             );
         }
-        self.update_git(cx);
+        self.git_diff_recalc(cx);
         cx.emit(Event::Reloaded);
         cx.notify();
     }
@@ -663,9 +662,8 @@ impl Buffer {
         task
     }
 
-    pub fn update_git(&mut self, cx: &mut ModelContext<Self>) {
-        //Grab head text
-
+    pub fn update_head_text(&mut self, head_text: Option<String>, cx: &mut ModelContext<Self>) {
+        self.head_text = head_text;
         self.git_diff_recalc(cx);
     }
 
@@ -674,6 +672,7 @@ impl Buffer {
     }
 
     pub fn git_diff_recalc(&mut self, cx: &mut ModelContext<Self>) {
+        println!("recalc");
         if self.git_diff_status.update_in_progress {
             self.git_diff_status.update_requested = true;
             return;
@@ -2221,7 +2220,7 @@ impl BufferSnapshot {
     pub fn git_diff_hunks_in_range<'a>(
         &'a self,
         query_row_range: Range<u32>,
-    ) -> impl 'a + Iterator<Item = git::DiffHunk<u32>> {
+    ) -> impl 'a + Iterator<Item = git::diff::DiffHunk<u32>> {
         self.git_diff.hunks_in_range(query_row_range, self)
     }
 

crates/project/Cargo.toml 🔗

@@ -24,6 +24,7 @@ collections = { path = "../collections" }
 db = { path = "../db" }
 fsevent = { path = "../fsevent" }
 fuzzy = { path = "../fuzzy" }
+git = { path = "../git" }
 gpui = { path = "../gpui" }
 language = { path = "../language" }
 lsp = { path = "../lsp" }
@@ -52,8 +53,6 @@ smol = "1.2.5"
 thiserror = "1.0.29"
 toml = "0.5"
 rocksdb = "0.18"
-git2 = { version = "0.15", default-features = false }
-
 
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }

crates/project/src/fs.rs 🔗

@@ -1,11 +1,9 @@
 use anyhow::{anyhow, Result};
 use fsevent::EventStream;
 use futures::{future::BoxFuture, Stream, StreamExt};
-use language::git::libgit::{Repository, RepositoryOpenFlags};
 use language::LineEnding;
 use smol::io::{AsyncReadExt, AsyncWriteExt};
 use std::{
-    ffi::OsStr,
     io,
     os::unix::fs::MetadataExt,
     path::{Component, Path, PathBuf},
@@ -22,8 +20,6 @@ use futures::lock::Mutex;
 #[cfg(any(test, feature = "test-support"))]
 use std::sync::{Arc, Weak};
 
-use crate::git_repository::GitRepository;
-
 #[async_trait::async_trait]
 pub trait Fs: Send + Sync {
     async fn create_dir(&self, path: &Path) -> Result<()>;

crates/project/src/project.rs 🔗

@@ -1,5 +1,4 @@
 pub mod fs;
-mod git_repository;
 mod ignore;
 mod lsp_command;
 pub mod search;
@@ -13,7 +12,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
 use clock::ReplicaId;
 use collections::{hash_map, BTreeMap, HashMap, HashSet};
 use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt};
-use git_repository::GitRepository;
+use git::repository::GitRepository;
 use gpui::{
     AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
     MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle,
@@ -4538,6 +4537,7 @@ impl Project {
             cx.subscribe(worktree, |this, worktree, event, cx| match event {
                 worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx),
                 worktree::Event::UpdatedGitRepositories(updated_repos) => {
+                    println!("{updated_repos:#?}");
                     this.update_local_worktree_buffers_git_repos(updated_repos, cx)
                 }
             })
@@ -4649,24 +4649,35 @@ impl Project {
 
     fn update_local_worktree_buffers_git_repos(
         &mut self,
-        updated_repos: &[GitRepository],
+        repos: &[GitRepository],
         cx: &mut ModelContext<Self>,
     ) {
-        for (buffer_id, buffer) in &self.opened_buffers {
+        //TODO: Produce protos
+
+        for (_, buffer) in &self.opened_buffers {
             if let Some(buffer) = buffer.upgrade(cx) {
-                buffer.update(cx, |buffer, cx| {
-                    let updated = updated_repos.iter().any(|repo| {
-                        buffer
-                            .file()
-                            .and_then(|file| file.as_local())
-                            .map(|file| repo.manages(&file.abs_path(cx)))
-                            .unwrap_or(false)
-                    });
+                let file = match buffer.read(cx).file().and_then(|file| file.as_local()) {
+                    Some(file) => file,
+                    None => return,
+                };
+                let path = file.path().clone();
+                let abs_path = file.abs_path(cx);
+                println!("got file");
 
-                    if updated {
-                        buffer.update_git(cx);
-                    }
-                });
+                let repo = match repos.iter().find(|repo| repo.manages(&abs_path)) {
+                    Some(repo) => repo.clone(),
+                    None => return,
+                };
+                println!("got repo");
+
+                cx.spawn(|_, mut cx| async move {
+                    let head_text = repo.load_head_text(&path).await;
+                    buffer.update(&mut cx, |buffer, cx| {
+                        println!("got calling update");
+                        buffer.update_head_text(head_text, cx);
+                    });
+                })
+                .detach();
             }
         }
     }

crates/project/src/worktree.rs 🔗

@@ -1,10 +1,9 @@
-use crate::{copy_recursive, git_repository::GitRepository, ProjectEntryId, RemoveOptions};
-
 use super::{
     fs::{self, Fs},
     ignore::IgnoreStack,
     DiagnosticSummary,
 };
+use crate::{copy_recursive, ProjectEntryId, RemoveOptions};
 use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
 use anyhow::{anyhow, Context, Result};
 use client::{proto, Client};
@@ -18,6 +17,8 @@ use futures::{
     Stream, StreamExt,
 };
 use fuzzy::CharBag;
+use git::repository::GitRepository;
+use git::{DOT_GIT, GITIGNORE};
 use gpui::{
     executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
     Task,
@@ -48,7 +49,7 @@ use std::{
     time::{Duration, SystemTime},
 };
 use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
-use util::{ResultExt, TryFutureExt, DOT_GIT, GITIGNORE};
+use util::{ResultExt, TryFutureExt};
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
 pub struct WorktreeId(usize);
@@ -523,7 +524,10 @@ impl LocalWorktree {
         match self.scan_state() {
             ScanState::Idle => {
                 let new_snapshot = self.background_snapshot.lock().clone();
-                let updated_repos = self.list_updated_repos(&new_snapshot);
+                let updated_repos = Self::list_updated_repos(
+                    &self.snapshot.git_repositories,
+                    &new_snapshot.git_repositories,
+                );
                 self.snapshot = new_snapshot;
 
                 if let Some(share) = self.share.as_mut() {
@@ -541,7 +545,10 @@ impl LocalWorktree {
                 let is_fake_fs = self.fs.is_fake();
 
                 let new_snapshot = self.background_snapshot.lock().clone();
-                let updated_repos = self.list_updated_repos(&new_snapshot);
+                let updated_repos = Self::list_updated_repos(
+                    &self.snapshot.git_repositories,
+                    &new_snapshot.git_repositories,
+                );
                 self.snapshot = new_snapshot;
 
                 self.poll_task = Some(cx.spawn_weak(|this, mut cx| async move {
@@ -573,16 +580,20 @@ impl LocalWorktree {
         cx.notify();
     }
 
-    fn list_updated_repos(&self, new_snapshot: &LocalSnapshot) -> Vec<GitRepository> {
-        let old_snapshot = &self.snapshot;
+    fn list_updated_repos(
+        old_repos: &[GitRepository],
+        new_repos: &[GitRepository],
+    ) -> Vec<GitRepository> {
+        println!("old repos: {:#?}", old_repos);
+        println!("new repos: {:#?}", new_repos);
 
         fn diff<'a>(
-            a: &'a LocalSnapshot,
-            b: &'a LocalSnapshot,
+            a: &'a [GitRepository],
+            b: &'a [GitRepository],
             updated: &mut HashMap<&'a Path, GitRepository>,
         ) {
-            for a_repo in &a.git_repositories {
-                let matched = b.git_repositories.iter().find(|b_repo| {
+            for a_repo in a {
+                let matched = b.iter().find(|b_repo| {
                     a_repo.git_dir_path() == b_repo.git_dir_path()
                         && a_repo.scan_id() == b_repo.scan_id()
                 });
@@ -595,10 +606,10 @@ impl LocalWorktree {
 
         let mut updated = HashMap::<&Path, GitRepository>::default();
 
-        diff(old_snapshot, new_snapshot, &mut updated);
-        diff(new_snapshot, old_snapshot, &mut updated);
+        diff(old_repos, new_repos, &mut updated);
+        diff(new_repos, old_repos, &mut updated);
 
-        updated.into_values().collect()
+        dbg!(updated.into_values().collect())
     }
 
     pub fn scan_complete(&self) -> impl Future<Output = ()> {
@@ -653,7 +664,7 @@ impl LocalWorktree {
                 settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked
             ) {
                 let results = if let Some(repo) = snapshot.repo_for(&abs_path) {
-                    repo.load_head_text(&abs_path).await
+                    repo.load_head_text(&path).await
                 } else {
                     None
                 };
@@ -1362,6 +1373,7 @@ impl LocalSnapshot {
     }
 
     pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepository> {
+        println!("chechking {path:?}");
         self.git_repositories
             .iter_mut()
             .rev() //git_repository is ordered lexicographically
@@ -1510,7 +1522,6 @@ impl LocalSnapshot {
         parent_path: Arc<Path>,
         entries: impl IntoIterator<Item = Entry>,
         ignore: Option<Arc<Gitignore>>,
-        fs: &dyn Fs,
     ) {
         let mut parent_entry = if let Some(parent_entry) =
             self.entries_by_path.get(&PathKey(parent_path.clone()), &())
@@ -2391,12 +2402,9 @@ impl BackgroundScanner {
             new_entries.push(child_entry);
         }
 
-        self.snapshot.lock().populate_dir(
-            job.path.clone(),
-            new_entries,
-            new_ignore,
-            self.fs.as_ref(),
-        );
+        self.snapshot
+            .lock()
+            .populate_dir(job.path.clone(), new_entries, new_ignore);
         for new_job in new_jobs {
             job.scan_queue.send(new_job).await.unwrap();
         }
@@ -2595,7 +2603,7 @@ impl BackgroundScanner {
             .git_repositories
             .iter()
             .cloned()
-            .filter(|repo| git2::Repository::open(repo.git_dir_path()).is_ok())
+            .filter(|repo| git::libgit::Repository::open(repo.git_dir_path()).is_ok())
             .collect();
 
         snapshot.git_repositories = new_repos;

crates/util/src/lib.rs 🔗

@@ -2,20 +2,13 @@
 pub mod test;
 
 use futures::Future;
-use lazy_static::lazy_static;
 use std::{
     cmp::Ordering,
-    ffi::OsStr,
     ops::AddAssign,
     pin::Pin,
     task::{Context, Poll},
 };
 
-lazy_static! {
-    pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git");
-    pub static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
-}
-
 pub fn truncate(s: &str, max_chars: usize) -> &str {
     match s.char_indices().nth(max_chars) {
         None => s,

crates/util/src/test.rs 🔗

@@ -2,14 +2,15 @@ mod assertions;
 mod marked_text;
 
 use git2;
-use std::path::{Path, PathBuf};
+use std::{
+    ffi::OsStr,
+    path::{Path, PathBuf},
+};
 use tempdir::TempDir;
 
 pub use assertions::*;
 pub use marked_text::*;
 
-use crate::DOT_GIT;
-
 pub fn temp_tree(tree: serde_json::Value) -> TempDir {
     let dir = TempDir::new("").unwrap();
     write_tree(dir.path(), tree);
@@ -28,7 +29,7 @@ fn write_tree(path: &Path, tree: serde_json::Value) {
                 Value::Object(_) => {
                     fs::create_dir(&path).unwrap();
 
-                    if path.file_name() == Some(&DOT_GIT) {
+                    if path.file_name() == Some(&OsStr::new(".git")) {
                         git2::Repository::init(&path.parent().unwrap()).unwrap();
                     }