diff mbox series

[RFC,4/6] contrib/cgit-rs: add repo initialization and config access

Message ID b84a8210a05c2358dc29f24a56adcbeaa90c8654.1723054623.git.steadmon@google.com (mailing list archive)
State Superseded
Headers show
Series Introduce cgit-rs, a Rust wrapper around libgit.a | expand

Commit Message

Josh Steadmon Aug. 7, 2024, 6:21 p.m. UTC
From: Calvin Wan <calvinwan@google.com>

Co-authored-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Josh Steadmon <steadmon@google.com>
---
 contrib/cgit-rs/public_symbol_export.c | 27 ++++++++++++++++++++++++++
 contrib/cgit-rs/public_symbol_export.h |  8 ++++++++
 contrib/cgit-rs/src/lib.rs             | 13 ++++++++++++-
 contrib/cgit-rs/src/main.rs            | 23 +++++++++++++++++++++-
 4 files changed, 69 insertions(+), 2 deletions(-)

Comments

brian m. carlson Aug. 7, 2024, 9:26 p.m. UTC | #1
On 2024-08-07 at 18:21:29, Josh Steadmon wrote:
> diff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs
> index dc46e7ff42..df350e758f 100644
> --- a/contrib/cgit-rs/src/lib.rs
> +++ b/contrib/cgit-rs/src/lib.rs
> @@ -1,6 +1,17 @@
> -use libc::c_char;
> +use libc::{c_char, c_int};
>  
>  extern "C" {
> +    pub fn libgit_setup_git_directory() -> *const c_char;
> +
> +    // From config.c
> +    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;
> +
> +    // From repository.c
> +    pub fn libgit_initialize_the_repository();
> +
> +    // From parse.c
> +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;
> +
>      // From version.c
>      pub fn libgit_user_agent() -> *const c_char;
>      pub fn libgit_user_agent_sanitized() -> *const c_char;

I think it would be helpful if we didn't expose the raw C API in Rust.
Nobody is going to want to write code that uses that in Rust.

If we _do_ expose that, it should be in a separate cgit-sys crate, which
is the customary naming, that exposes only that and then we can offer
nicer wrappers around it.

> diff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs
> index 1794e3f43e..c5f8644fca 100644
> --- a/contrib/cgit-rs/src/main.rs
> +++ b/contrib/cgit-rs/src/main.rs
> @@ -1,4 +1,4 @@
> -use std::ffi::CStr;
> +use std::ffi::{CStr, CString};
>  
>  fn main() {
>      println!("Let's print some strings provided by Git");
> @@ -7,4 +7,25 @@ fn main() {
>      println!("git_user_agent() = {:?}", c_str);
>      println!("git_user_agent_sanitized() = {:?}",
>          unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });
> +
> +    println!("\nNow try passing args");
> +    let test_arg = CString::new("test_arg").unwrap();
> +    println!("git_parse_maybe_bool(...) = {:?}",
> +        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });
> +
> +    println!("\nCan we get an int out of our config??");
> +    unsafe {
> +        cgit::libgit_initialize_the_repository();
> +        cgit::libgit_setup_git_directory();
> +        let mut val: libc::c_int = 0;
> +        let key = CString::new("trace2.eventNesting").unwrap();
> +        cgit::libgit_config_get_int(
> +            key.as_ptr(),
> +            &mut val as *mut i32
> +        );
> +        println!(
> +            "git_config_get_int(\"trace2.eventNesting\") -> {:?}",
> +            val
> +        );
> +    };
>  }

It seems like this code wants to be a set of tests and maybe it would
be helpful to write it as a few instead.  For example, we can assume
that our user-agent starts with `git/` assuming it hasn't been
overridden, so maybe we could write that as a test, or at least that we
got a valid C string.
Josh Steadmon Aug. 7, 2024, 11:14 p.m. UTC | #2
On 2024.08.07 21:26, brian m. carlson wrote:
> On 2024-08-07 at 18:21:29, Josh Steadmon wrote:
> > diff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs
> > index dc46e7ff42..df350e758f 100644
> > --- a/contrib/cgit-rs/src/lib.rs
> > +++ b/contrib/cgit-rs/src/lib.rs
> > @@ -1,6 +1,17 @@
> > -use libc::c_char;
> > +use libc::{c_char, c_int};
> >  
> >  extern "C" {
> > +    pub fn libgit_setup_git_directory() -> *const c_char;
> > +
> > +    // From config.c
> > +    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;
> > +
> > +    // From repository.c
> > +    pub fn libgit_initialize_the_repository();
> > +
> > +    // From parse.c
> > +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;
> > +
> >      // From version.c
> >      pub fn libgit_user_agent() -> *const c_char;
> >      pub fn libgit_user_agent_sanitized() -> *const c_char;
> 
> I think it would be helpful if we didn't expose the raw C API in Rust.
> Nobody is going to want to write code that uses that in Rust.
> 
> If we _do_ expose that, it should be in a separate cgit-sys crate, which
> is the customary naming, that exposes only that and then we can offer
> nicer wrappers around it.

Yeah, that's a known NEEDSWORK that I forgot to mention in the cover
letter. I'm also going to get in touch soon with the gitoxide &
libgit2-rs folks to see if there's any joint work that we can do in
terms of defining nicer Rust APIs.

Semi-relatedly, I was wondering if you might be able to answer a cargo /
crates.io question: for a crate such as cgit-rs, which is not located in
the root of its VCS repo, will cargo balk at downloading the full
git.git worktree? Our build.rs expects the full Git source to be
available at "../.." relative to the crate root. We've currently only
tested consuming cgit-rs in JJ via a local path rather than through
crates.io.

libgit2-rs avoids this issue by including the C source of libgit2 as a
submodule, but I'd prefer for cgit-rs to live in the git.git repo if at
all possible.

> > diff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs
> > index 1794e3f43e..c5f8644fca 100644
> > --- a/contrib/cgit-rs/src/main.rs
> > +++ b/contrib/cgit-rs/src/main.rs
> > @@ -1,4 +1,4 @@
> > -use std::ffi::CStr;
> > +use std::ffi::{CStr, CString};
> >  
> >  fn main() {
> >      println!("Let's print some strings provided by Git");
> > @@ -7,4 +7,25 @@ fn main() {
> >      println!("git_user_agent() = {:?}", c_str);
> >      println!("git_user_agent_sanitized() = {:?}",
> >          unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });
> > +
> > +    println!("\nNow try passing args");
> > +    let test_arg = CString::new("test_arg").unwrap();
> > +    println!("git_parse_maybe_bool(...) = {:?}",
> > +        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });
> > +
> > +    println!("\nCan we get an int out of our config??");
> > +    unsafe {
> > +        cgit::libgit_initialize_the_repository();
> > +        cgit::libgit_setup_git_directory();
> > +        let mut val: libc::c_int = 0;
> > +        let key = CString::new("trace2.eventNesting").unwrap();
> > +        cgit::libgit_config_get_int(
> > +            key.as_ptr(),
> > +            &mut val as *mut i32
> > +        );
> > +        println!(
> > +            "git_config_get_int(\"trace2.eventNesting\") -> {:?}",
> > +            val
> > +        );
> > +    };
> >  }
> 
> It seems like this code wants to be a set of tests and maybe it would
> be helpful to write it as a few instead.  For example, we can assume
> that our user-agent starts with `git/` assuming it hasn't been
> overridden, so maybe we could write that as a test, or at least that we
> got a valid C string.

Agreed.

> -- 
> brian m. carlson (they/them or he/him)
> Toronto, Ontario, CA
brian m. carlson Aug. 8, 2024, 12:04 a.m. UTC | #3
On 2024-08-07 at 23:14:43, Josh Steadmon wrote:
> Semi-relatedly, I was wondering if you might be able to answer a cargo /
> crates.io question: for a crate such as cgit-rs, which is not located in
> the root of its VCS repo, will cargo balk at downloading the full
> git.git worktree? Our build.rs expects the full Git source to be
> available at "../.." relative to the crate root. We've currently only
> tested consuming cgit-rs in JJ via a local path rather than through
> crates.io.

The documentation[0] says this:

  Cargo fetches the git repository at that location and traverses the
  file tree to find Cargo.toml file for the requested crate anywhere
  inside the git repository. For example, regex-lite and regex-syntax
  are members of rust-lang/regex repo and can be referred to by the
  repo’s root URL (https://github.com/rust-lang/regex.git) regardless of
  where in the file tree they reside.

If you want to test, you should be able to push it to a repository
somewhere, even if that's a local path, and specify like so to test:

----
[dependencies]
cgit-rs = { version = "0.1.0", git = "file:///path/to/git.git", branch = "cgit-rs" }
----

and see if it finds it.

[0] https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
diff mbox series

Patch

diff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c
index 3d1cd6cc4f..ab3401ac40 100644
--- a/contrib/cgit-rs/public_symbol_export.c
+++ b/contrib/cgit-rs/public_symbol_export.c
@@ -2,11 +2,35 @@ 
 // original symbols can be hidden. Renaming these with a "libgit_" prefix also
 // avoid conflicts with other libraries such as libgit2.
 
+#include "git-compat-util.h"
 #include "contrib/cgit-rs/public_symbol_export.h"
+#include "attr.h"
+#include "config.h"
+#include "setup.h"
 #include "version.h"
 
 #pragma GCC visibility push(default)
 
+const char *libgit_setup_git_directory(void)
+{
+	return setup_git_directory();
+}
+
+int libgit_config_get_int(const char *key, int *dest)
+{
+	return git_config_get_int(key, dest);
+}
+
+void libgit_initialize_the_repository(void)
+{
+	initialize_the_repository();
+}
+
+int libgit_parse_maybe_bool(const char *val)
+{
+	return git_parse_maybe_bool(val);
+}
+
 const char *libgit_user_agent(void)
 {
 	return git_user_agent();
@@ -17,4 +41,7 @@  const char *libgit_user_agent_sanitized(void)
 	return git_user_agent_sanitized();
 }
 
+const char *libgit_attr__true = git_attr__true;
+const char *libgit_attr__false = git_attr__false;
+
 #pragma GCC visibility pop
diff --git a/contrib/cgit-rs/public_symbol_export.h b/contrib/cgit-rs/public_symbol_export.h
index a3372f93fa..97e8e871ba 100644
--- a/contrib/cgit-rs/public_symbol_export.h
+++ b/contrib/cgit-rs/public_symbol_export.h
@@ -1,6 +1,14 @@ 
 #ifndef PUBLIC_SYMBOL_EXPORT_H
 #define PUBLIC_SYMBOL_EXPORT_H
 
+const char *libgit_setup_git_directory(void);
+
+int libgit_config_get_int(const char *key, int *dest);
+
+void libgit_initialize_the_repository(void);
+
+int libgit_parse_maybe_bool(const char *val);
+
 const char *libgit_user_agent(void);
 
 const char *libgit_user_agent_sanitized(void);
diff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs
index dc46e7ff42..df350e758f 100644
--- a/contrib/cgit-rs/src/lib.rs
+++ b/contrib/cgit-rs/src/lib.rs
@@ -1,6 +1,17 @@ 
-use libc::c_char;
+use libc::{c_char, c_int};
 
 extern "C" {
+    pub fn libgit_setup_git_directory() -> *const c_char;
+
+    // From config.c
+    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;
+
+    // From repository.c
+    pub fn libgit_initialize_the_repository();
+
+    // From parse.c
+    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;
+
     // From version.c
     pub fn libgit_user_agent() -> *const c_char;
     pub fn libgit_user_agent_sanitized() -> *const c_char;
diff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs
index 1794e3f43e..c5f8644fca 100644
--- a/contrib/cgit-rs/src/main.rs
+++ b/contrib/cgit-rs/src/main.rs
@@ -1,4 +1,4 @@ 
-use std::ffi::CStr;
+use std::ffi::{CStr, CString};
 
 fn main() {
     println!("Let's print some strings provided by Git");
@@ -7,4 +7,25 @@  fn main() {
     println!("git_user_agent() = {:?}", c_str);
     println!("git_user_agent_sanitized() = {:?}",
         unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });
+
+    println!("\nNow try passing args");
+    let test_arg = CString::new("test_arg").unwrap();
+    println!("git_parse_maybe_bool(...) = {:?}",
+        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });
+
+    println!("\nCan we get an int out of our config??");
+    unsafe {
+        cgit::libgit_initialize_the_repository();
+        cgit::libgit_setup_git_directory();
+        let mut val: libc::c_int = 0;
+        let key = CString::new("trace2.eventNesting").unwrap();
+        cgit::libgit_config_get_int(
+            key.as_ptr(),
+            &mut val as *mut i32
+        );
+        println!(
+            "git_config_get_int(\"trace2.eventNesting\") -> {:?}",
+            val
+        );
+    };
 }