├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── src └── main.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "git-glance" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Scott Chacon "] 6 | license = "MIT" 7 | description = "AI assisted GitHub PR-based Git changelog generator" 8 | readme = "README.md" 9 | homepage = "https://github.com/schacon/git-glance" 10 | repository = "https://github.com/schacon/git-glance" 11 | keywords = ["cli", "changelog", "git"] 12 | categories = ["command-line-utilities"] 13 | 14 | [dependencies] 15 | clap = { version = "4.5.8", features = ["derive"] } 16 | git2 = "0.19.0" 17 | anyhow = "1.0.86" 18 | colored = "2.1.0" 19 | indicatif = "0.17.8" 20 | console = "0.15.8" 21 | dialoguer = "0.11.0" 22 | serde = { version = "1.0", features = ["derive"] } 23 | serde_json = "1.0.120" 24 | openai-api-rs = "4.1.1" 25 | chrono = "0.4.38" 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Scott Chacon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git Glance 2 | 3 | This is a very simple Git changelog generator, but it is by far the best Git changelog generator that I have ever written. 4 | 5 | It does not rely on any specific style of commit messages (such as "conventional commits") and assumes that you're using GitHub pull requests as the main path to feature integration. 6 | 7 | It figures out what the commit range is you are trying to generate a changelog for, then gathers all the associated pull request data, then generates tagged summaries via OpenAI. 8 | 9 | ![preparing the message](https://github.com/schacon/git-glance/assets/70/b93e513a-cd45-4ab4-bec7-44ece96aa2af) 10 | 11 | Once all that data is gathered, it will output a markdown based changelog with links to relevant PRs. 12 | 13 | ![markdown output](https://github.com/schacon/git-glance/assets/70/1541dc29-c748-43f6-8638-f90875d1cd17) 14 | 15 | ## Installing 16 | 17 | ``` 18 | $ cargo install git-glance 19 | ``` 20 | 21 | ## Requirements 22 | 23 | In order to get the PR data, it assumes that you have `gh` cli tool setup and that we can execute it. 24 | 25 | It also uses OpenAI to help with classification and summarization. You will need an OpenAI key or it will bail. 26 | 27 | ``` 28 | $ git config --global --add glance.openai.key sk_blahblahblah 29 | ``` 30 | 31 | You can see if these things are set with `git-glance --check`: 32 | 33 | ![glance check](https://github.com/schacon/git-glance/assets/70/93ac2f2b-83f1-4369-a696-a1052dbf0bd0) 34 | 35 | ## Warnings 36 | 37 | This is horrible, horrible software and it will probably break. I'm not great at Rust and I've done little testing. It works for me, but if you're looking for solid code, this isn't a great place to look. Have fun. :) 38 | 39 | ## TODO 40 | 41 | - [ ] Add executive overview 42 | - [ ] Capitalize everything 43 | - [ ] Always do 'features' first 44 | - [ ] Sort by something (date? size of diff?) 45 | - [ ] Send diffs if we don't have much of a description 46 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // git-glance 2 | // 3 | // - take the commit range 4 | // - determine all involved commits 5 | // - find related PR information 6 | // - see if we can group commits into PRs 7 | // - create prompt for each PR/group 8 | // - ask AI for a one line summary 9 | // - determine feature, bug fix, documentation, etc 10 | // - compose release notes and output markdown 11 | // - optionally with debug information (which commits/pr) 12 | 13 | use anyhow::bail; 14 | use clap::Parser; 15 | use colored::Colorize; 16 | use git2::Repository; 17 | 18 | use openai_api_rs::v1::api::Client; 19 | use openai_api_rs::v1::chat_completion::{self, ChatCompletionRequest}; 20 | use openai_api_rs::v1::common::GPT4_O; 21 | use serde::{Deserialize, Serialize}; 22 | use std::collections::HashMap; 23 | use std::process::{exit, Stdio}; 24 | use std::{fmt::Write, io::Write as ioWrite}; 25 | 26 | use indicatif::{ProgressBar, ProgressState, ProgressStyle}; 27 | 28 | #[derive(Parser, Debug)] 29 | #[command(version, about, long_about = None)] 30 | struct Cli { 31 | #[arg(short, long)] 32 | release: Option, 33 | 34 | #[arg(short, long)] 35 | last: Option, 36 | 37 | #[arg(long)] 38 | check: bool, 39 | } 40 | 41 | #[derive(Serialize, Deserialize, Debug)] 42 | struct CommitInfo { 43 | oid: String, 44 | headline: String, 45 | body: String, 46 | pr: Option, 47 | } 48 | 49 | #[derive(Serialize, Deserialize, Debug)] 50 | struct PrInfo { 51 | number: String, 52 | title: String, 53 | body: String, 54 | author: String, 55 | comments: Vec, 56 | commits: Vec, 57 | url: String, 58 | updated_at: String, 59 | merged_at: String, 60 | } 61 | 62 | #[derive(Debug)] 63 | struct PrTaggedSummary { 64 | tag: String, 65 | summary: String, 66 | number: String, 67 | url: String, 68 | } 69 | 70 | fn main() -> Result<(), anyhow::Error> { 71 | let cli = Cli::parse(); 72 | let repo = Repository::open_from_env().unwrap(); 73 | 74 | if cli.check { 75 | check_setup(&repo); 76 | exit(0) 77 | } 78 | 79 | // make the dirs we need if they're not there 80 | std::fs::create_dir_all(repo.path().join("glance/commits"))?; 81 | std::fs::create_dir_all(repo.path().join("glance/prs"))?; 82 | 83 | // get the commit list 84 | println!("{}", "Here is what I'm working with:".green()); 85 | // first, get the tip of the branch (or the -r release sha specified) 86 | let tip = match &cli.release { 87 | Some(release) => repo.revparse_single(release).unwrap(), 88 | None => repo.revparse_single("HEAD").unwrap(), 89 | }; 90 | println!("Tip commit: {}", tip.id().to_string().blue()); 91 | 92 | // then, get the last commit (-l last sha specified or last tag) 93 | // TODO: actually order by tag date 94 | let last = match (cli.last, repo.tag_names(None)?.iter().last()) { 95 | (Some(sha), _) => repo.revparse_single(&sha).unwrap(), 96 | (_, Some(Some(last_tag))) => repo.revparse_single(last_tag).unwrap(), 97 | (_, _) => bail!("no tags found and no last release specified"), 98 | }; 99 | println!("Last commit: {}", last.id().to_string().blue()); 100 | 101 | // get the commit range 102 | let mut revwalk = repo.revwalk()?; 103 | revwalk.push(tip.id())?; 104 | revwalk.hide(last.id())?; 105 | 106 | let commits: Vec<_> = revwalk.collect::, _>>()?; 107 | 108 | //count the vec 109 | let count = commits.len(); 110 | println!( 111 | "Number of commits in release: {}", 112 | count.to_string().green() 113 | ); 114 | 115 | commits.clone().into_iter().for_each(|commit| { 116 | let commit = repo.find_commit(commit).unwrap(); 117 | let message = commit.summary().unwrap(); 118 | println!("{} {}", commit.id().to_string().blue(), message); 119 | }); 120 | 121 | println!(" "); 122 | println!("{}", "Getting PR information for commits".green()); 123 | 124 | let pb = ProgressBar::new(count.try_into().unwrap()); 125 | pb.set_style( 126 | ProgressStyle::with_template( 127 | "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} ({eta})", 128 | ) 129 | .unwrap() 130 | .with_key("eta", |state: &ProgressState, w: &mut dyn Write| { 131 | write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap() 132 | }) 133 | .progress_chars("#>-"), 134 | ); 135 | 136 | // get github PR information 137 | 138 | let mut pr_list = HashMap::new(); 139 | let mut commit_list = HashMap::new(); 140 | 141 | let mut pos = 0; 142 | commits.clone().into_iter().for_each(|commit| { 143 | pos += 1; 144 | pb.set_position(pos); 145 | match get_pr_info(&repo, commit) { 146 | Ok(pr_info) => match pr_info { 147 | Some(pr_info) => { 148 | pr_list.insert(pr_info.number.clone(), pr_info); 149 | } 150 | None => { 151 | commit_list.insert(commit.to_string(), get_commit_info(&repo, commit).unwrap()); 152 | } 153 | }, 154 | Err(e) => { 155 | println!("Error: {}", e); 156 | } 157 | } 158 | }); 159 | 160 | pb.finish_with_message("downloaded"); 161 | 162 | println!(" "); 163 | println!("{}", "Summarizing".green()); 164 | 165 | let pr_count = pr_list.len(); 166 | let pb = ProgressBar::new(pr_count.try_into().unwrap()); 167 | pb.set_style( 168 | ProgressStyle::with_template( 169 | "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} ({eta})", 170 | ) 171 | .unwrap() 172 | .with_key("eta", |state: &ProgressState, w: &mut dyn Write| { 173 | write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap() 174 | }) 175 | .progress_chars("#>-"), 176 | ); 177 | 178 | let mut pos = 0; 179 | 180 | // collect all the pr_infos into a vector of pr_summaries 181 | let pr_summaries: Vec<_> = pr_list 182 | .values() 183 | .map(|pr_info| pr_to_tagged_summary(&repo, pr_info)) 184 | // also increment the progress bar 185 | .inspect(|_| { 186 | pos += 1; 187 | pb.set_position(pos); 188 | }) 189 | .collect(); 190 | 191 | pb.finish_with_message("summarized"); 192 | 193 | println!(" "); 194 | println!("{}", "Changelog".green()); 195 | 196 | // if there is a tag on the tip commit, show it 197 | if let Some(release) = &cli.release { 198 | // get the date 199 | let commit = repo.revparse_single(release).unwrap(); 200 | let commit = repo.find_commit(commit.id()).unwrap(); 201 | let time = commit.time().seconds(); 202 | let time = chrono::DateTime::::from_timestamp(time, 0); 203 | if let Some(time) = time { 204 | // format like "June 3, 2024" 205 | let time = time.format("%B %e, %Y").to_string(); 206 | println!("**{}** ({})", release, time); 207 | } else { 208 | println!("**{}**", release); 209 | } 210 | }; 211 | 212 | // group the summaries by tag field 213 | let mut grouped_pr_summaries = HashMap::new(); 214 | for pr_summary in pr_summaries.iter() { 215 | match pr_summary { 216 | Ok(pr_summary) => { 217 | let tag = pr_summary.tag.clone(); 218 | grouped_pr_summaries 219 | .entry(tag) 220 | .or_insert_with(Vec::new) 221 | .push(pr_summary); 222 | } 223 | Err(e) => { 224 | println!("Error: {}", e); 225 | } 226 | } 227 | } 228 | 229 | // print out the summaries by group 230 | for (tag, pr_summaries) in grouped_pr_summaries.iter() { 231 | // capitalize the first letter in the tag 232 | let tag = tag.chars().next().unwrap().to_uppercase().to_string() + &tag[1..]; 233 | println!("\n** {} **", tag.magenta()); 234 | for &pr_summary in pr_summaries { 235 | println!( 236 | "* {} [#{}]({})", 237 | pr_summary.summary, 238 | pr_summary.number.blue(), 239 | pr_summary.url 240 | ); 241 | } 242 | } 243 | 244 | if !commit_list.is_empty() { 245 | println!("## {}", "Other".magenta()); 246 | } 247 | // print out the commits 248 | for (commit_oid, commit_info) in commit_list.iter() { 249 | let short_oid = &commit_oid[..6]; 250 | println!("* {} ({})", commit_info.headline, short_oid); 251 | } 252 | 253 | Ok(()) 254 | } 255 | 256 | // check `gh` works 257 | // check openai key 258 | fn check_setup(repo: &Repository) { 259 | let config = repo.config().unwrap(); 260 | let openai_key = config.get_string("glance.openai.key"); 261 | match openai_key { 262 | Ok(_) => { 263 | println!("{}", "* OpenAI key found".green()); 264 | } 265 | Err(_) => { 266 | println!("{}", "* OpenAI key not found".red()); 267 | println!( 268 | "{}", 269 | " - please get an openai api key and install it with: 270 | git config --global --add glance.openai.key sk_mykeydata\n" 271 | .blue() 272 | ); 273 | } 274 | } 275 | 276 | let mut cmd = std::process::Command::new("gh"); 277 | cmd.args(["auth", "status"]); 278 | cmd.stderr(Stdio::piped()); 279 | cmd.stdout(Stdio::piped()); 280 | cmd.stdin(Stdio::null()); 281 | 282 | let child = cmd.spawn(); 283 | if child.is_err() { 284 | println!("{}", "* gh not found".red()); 285 | println!( 286 | "{}", 287 | " - please install gh from https://cli.github.com/".blue() 288 | ); 289 | return; 290 | } 291 | 292 | let output = child.unwrap().wait_with_output().unwrap(); 293 | 294 | if output.status.success() { 295 | let stderr = String::from_utf8_lossy(&output.stderr); 296 | let stdout = String::from_utf8_lossy(&output.stdout); 297 | let std_both = format!("{} {}", stdout, stderr); 298 | println!("{}\n\n {}", "* gh auth status good".green(), std_both); 299 | } else { 300 | let stderr = String::from_utf8_lossy(&output.stderr); 301 | let stdout = String::from_utf8_lossy(&output.stdout); 302 | let std_both = format!("{} {}", stdout, stderr); 303 | println!("{}\n\n {}", "* Failed to run gh".red(), std_both); 304 | } 305 | } 306 | 307 | fn pr_to_tagged_summary(repo: &Repository, pr: &PrInfo) -> Result { 308 | let commits = pr 309 | .commits 310 | .iter() 311 | .map(|commit| format!("* {}", commit.headline)) 312 | .collect::>() 313 | .join("\n"); 314 | 315 | let prompt = format!( 316 | "You are a senior software developer writing a one line summary and tag for a pull request. 317 | I will give you a Pull Request title, body and a list of the commit messages. 318 | Write me a tag and one line summary for that pull request in the following json format: 319 | 320 | ``` 321 | {{ 322 | 'tag': 'feature', 323 | 'summary': 'updated the css to remove all tailwind references' 324 | }} 325 | ``` 326 | 327 | The tag in parenthesis should be one of: feature, bugfix, documentation, test, misc. 328 | 329 | Here is the pull request information: 330 | 331 | Title: {} 332 | Body: 333 | {} 334 | 335 | Commit Summaries: 336 | {} 337 | 338 | Please respond with only the json data of tag and summary", 339 | pr.title, pr.body, commits, 340 | ); 341 | 342 | let response = get_ai_response(&repo, prompt)?; 343 | 344 | // parse the json 345 | // we need to strip the ```json\n``` markdown stuff 346 | let response = response.replace("```json\n", "").replace("```", ""); 347 | let response: serde_json::Value = serde_json::from_str(&response)?; 348 | let tag = response["tag"].as_str().unwrap(); 349 | let summary = response["summary"].as_str().unwrap(); 350 | 351 | Ok(PrTaggedSummary { 352 | summary: summary.to_string(), 353 | tag: tag.to_string(), 354 | number: pr.number.clone(), 355 | url: pr.url.clone(), 356 | }) 357 | } 358 | 359 | fn get_ai_response(repo: &Repository, prompt: String) -> Result { 360 | let config = repo.config()?; 361 | 362 | /* 363 | let ai_method = match config.get_string("glance.ai") { 364 | Ok(ai_method) => ai_method, 365 | Err(_) => bail!("no ai method configured in git config\nuse `git config --add glance.ai [openai,claude,ollama]` to set one\nthen run git config --add glance.openai.key [openai-key]"), 366 | }; 367 | println!("Using AI method: {}", ai_method); 368 | */ 369 | 370 | let openai_key = config.get_string("glance.openai.key")?; 371 | let client = Client::new(openai_key); 372 | let req = ChatCompletionRequest::new( 373 | GPT4_O.to_string(), 374 | vec![chat_completion::ChatCompletionMessage { 375 | role: chat_completion::MessageRole::user, 376 | content: chat_completion::Content::Text(prompt), 377 | name: None, 378 | }], 379 | ); 380 | let result = client.chat_completion(req)?; 381 | return Ok(result.choices[0] 382 | .message 383 | .content 384 | .as_ref() 385 | .unwrap() 386 | .to_string()); 387 | } 388 | 389 | fn get_commit_info(repo: &Repository, commit: git2::Oid) -> Result { 390 | let commit_object = repo.find_commit(commit)?; 391 | let commit_info = CommitInfo { 392 | oid: commit.to_string(), 393 | headline: commit_object.summary().unwrap().to_string(), 394 | body: commit_object.message().unwrap().to_string(), 395 | pr: None, 396 | }; 397 | Ok(commit_info) 398 | } 399 | 400 | // look for cached data for this commit oid in .git/glance/commits/[oid].json 401 | // if it exists, return it 402 | // if it doesn't exist, run gh pr list --json --search [oid] --state merged 403 | // and cache the result 404 | fn get_pr_info(repo: &Repository, commit: git2::Oid) -> Result, anyhow::Error> { 405 | let commit_path = repo 406 | .path() 407 | .join("glance/commits") 408 | .join(commit.to_string() + ".json"); 409 | 410 | let commit_object = repo.find_commit(commit)?; 411 | 412 | if commit_path.exists() { 413 | let file = std::fs::File::open(commit_path)?; 414 | let reader = std::io::BufReader::new(file); 415 | let commit_info: CommitInfo = serde_json::from_reader(reader)?; 416 | let pr_info = match commit_info.pr { 417 | Some(pr) => { 418 | let pr_path = repo.path().join("glance/prs").join(pr + ".json"); 419 | let file = std::fs::File::open(pr_path)?; 420 | let reader = std::io::BufReader::new(file); 421 | let pr_info: PrInfo = serde_json::from_reader(reader)?; 422 | Some(pr_info) 423 | } 424 | None => None, 425 | }; 426 | return Ok(pr_info); 427 | } else { 428 | let gh_program = "gh"; 429 | let mut cmd = std::process::Command::new(gh_program); 430 | cmd.args([ 431 | "pr", 432 | "list", 433 | "--json", 434 | "number,title,author,body,comments,commits,url,updatedAt,mergedAt", 435 | "--search", 436 | &commit.to_string(), 437 | "--state", 438 | "merged", 439 | ]); 440 | 441 | cmd.stderr(Stdio::piped()); 442 | cmd.stdout(Stdio::piped()); 443 | cmd.stdin(Stdio::null()); 444 | 445 | let child = cmd.spawn().unwrap(); 446 | let output = child.wait_with_output().unwrap(); 447 | 448 | if output.status.success() { 449 | let stdout = String::from_utf8_lossy(&output.stdout); 450 | let pr_info: serde_json::Value = serde_json::from_str(stdout.as_ref())?; 451 | if pr_info[0] == serde_json::Value::Null { 452 | return Ok(None); 453 | } 454 | 455 | let commits = pr_info[0]["commits"] 456 | .as_array() 457 | .unwrap() 458 | .iter() 459 | .map(|commit| CommitInfo { 460 | oid: commit["oid"].as_str().unwrap().to_string(), 461 | headline: commit["messageHeadline"].as_str().unwrap().to_string(), 462 | body: commit["messageBody"].as_str().unwrap().to_string(), 463 | pr: Some(pr_info[0]["number"].to_string()), 464 | }) 465 | .collect(); 466 | 467 | let pr_data = PrInfo { 468 | number: pr_info[0]["number"].to_string(), 469 | title: pr_info[0]["title"].as_str().unwrap().to_string(), 470 | body: pr_info[0]["body"].as_str().unwrap().to_string(), 471 | author: pr_info[0]["author"]["login"].as_str().unwrap().to_string(), 472 | updated_at: pr_info[0]["updatedAt"].as_str().unwrap().to_string(), 473 | merged_at: pr_info[0]["mergedAt"].as_str().unwrap().to_string(), 474 | commits, 475 | comments: vec![], 476 | url: pr_info[0]["url"].as_str().unwrap().to_string(), 477 | }; 478 | 479 | let pr_path = repo 480 | .path() 481 | .join("glance/prs") 482 | .join(pr_info[0]["number"].to_string() + ".json"); 483 | let mut file = std::fs::File::create(pr_path)?; 484 | file.write_all(serde_json::to_string(&pr_data)?.as_bytes())?; 485 | 486 | let commit_cache = CommitInfo { 487 | oid: commit.to_string(), 488 | headline: commit_object.summary().unwrap().to_string(), 489 | body: commit_object.message().unwrap().to_string(), 490 | pr: Some(pr_info[0]["number"].to_string()), 491 | }; 492 | let commit_cache_path = repo 493 | .path() 494 | .join("glance/commits") 495 | .join(commit.to_string() + ".json"); 496 | let mut file = std::fs::File::create(commit_cache_path).unwrap(); 497 | file.write_all(serde_json::to_string(&commit_cache).unwrap().as_bytes()) 498 | .unwrap(); 499 | 500 | let commits = pr_info[0]["commits"].as_array(); 501 | match commits { 502 | Some(commits) => { 503 | commits.iter().for_each(|commit| { 504 | let commit_cache = CommitInfo { 505 | oid: commit["oid"].as_str().unwrap().to_string(), 506 | headline: commit["messageHeadline"].as_str().unwrap().to_string(), 507 | body: commit["messageBody"].as_str().unwrap().to_string(), 508 | pr: Some(pr_info[0]["number"].to_string()), 509 | }; 510 | let commit_cache_path = repo 511 | .path() 512 | .join("glance/commits") 513 | .join(commit["oid"].as_str().unwrap().to_string() + ".json"); 514 | let mut file = std::fs::File::create(commit_cache_path).unwrap(); 515 | file.write_all(serde_json::to_string(&commit_cache).unwrap().as_bytes()) 516 | .unwrap(); 517 | }); 518 | return Ok(Some(pr_data)); 519 | } 520 | None => { 521 | // nothing 522 | let commit_cache = CommitInfo { 523 | oid: commit.to_string(), 524 | headline: commit_object.summary().unwrap().to_string(), 525 | body: commit_object.message().unwrap().to_string(), 526 | pr: None, 527 | }; 528 | let commit_cache_path = repo 529 | .path() 530 | .join("glance/commits") 531 | .join(commit.to_string() + ".json"); 532 | let mut file = std::fs::File::create(commit_cache_path).unwrap(); 533 | file.write_all(serde_json::to_string(&commit_cache).unwrap().as_bytes()) 534 | .unwrap(); 535 | } 536 | } 537 | } else { 538 | let stderr = String::from_utf8_lossy(&output.stderr); 539 | let stdout = String::from_utf8_lossy(&output.stdout); 540 | let std_both = format!("{} {}", stdout, stderr); 541 | bail!("Failed to run gh: {}", std_both); 542 | } 543 | } 544 | 545 | Ok(None) 546 | } 547 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "android-tzdata" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 10 | 11 | [[package]] 12 | name = "android_system_properties" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "is_terminal_polyfill", 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.7" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" 40 | 41 | [[package]] 42 | name = "anstyle-parse" 43 | version = "0.2.4" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" 46 | dependencies = [ 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-query" 52 | version = "1.1.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" 55 | dependencies = [ 56 | "windows-sys 0.52.0", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-wincon" 61 | version = "3.0.3" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" 64 | dependencies = [ 65 | "anstyle", 66 | "windows-sys 0.52.0", 67 | ] 68 | 69 | [[package]] 70 | name = "anyhow" 71 | version = "1.0.86" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 74 | 75 | [[package]] 76 | name = "autocfg" 77 | version = "1.3.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 80 | 81 | [[package]] 82 | name = "base64" 83 | version = "0.12.3" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 86 | 87 | [[package]] 88 | name = "bitflags" 89 | version = "2.6.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 92 | 93 | [[package]] 94 | name = "bumpalo" 95 | version = "3.16.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 98 | 99 | [[package]] 100 | name = "cc" 101 | version = "1.0.103" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410" 104 | dependencies = [ 105 | "jobserver", 106 | "libc", 107 | "once_cell", 108 | ] 109 | 110 | [[package]] 111 | name = "cfg-if" 112 | version = "1.0.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 115 | 116 | [[package]] 117 | name = "chrono" 118 | version = "0.4.38" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 121 | dependencies = [ 122 | "android-tzdata", 123 | "iana-time-zone", 124 | "js-sys", 125 | "num-traits", 126 | "wasm-bindgen", 127 | "windows-targets 0.52.5", 128 | ] 129 | 130 | [[package]] 131 | name = "clap" 132 | version = "4.5.8" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" 135 | dependencies = [ 136 | "clap_builder", 137 | "clap_derive", 138 | ] 139 | 140 | [[package]] 141 | name = "clap_builder" 142 | version = "4.5.8" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" 145 | dependencies = [ 146 | "anstream", 147 | "anstyle", 148 | "clap_lex", 149 | "strsim", 150 | ] 151 | 152 | [[package]] 153 | name = "clap_derive" 154 | version = "4.5.8" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" 157 | dependencies = [ 158 | "heck", 159 | "proc-macro2", 160 | "quote", 161 | "syn", 162 | ] 163 | 164 | [[package]] 165 | name = "clap_lex" 166 | version = "0.7.1" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" 169 | 170 | [[package]] 171 | name = "colorchoice" 172 | version = "1.0.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" 175 | 176 | [[package]] 177 | name = "colored" 178 | version = "2.1.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 181 | dependencies = [ 182 | "lazy_static", 183 | "windows-sys 0.48.0", 184 | ] 185 | 186 | [[package]] 187 | name = "console" 188 | version = "0.15.8" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 191 | dependencies = [ 192 | "encode_unicode", 193 | "lazy_static", 194 | "libc", 195 | "unicode-width", 196 | "windows-sys 0.52.0", 197 | ] 198 | 199 | [[package]] 200 | name = "core-foundation-sys" 201 | version = "0.8.6" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 204 | 205 | [[package]] 206 | name = "dialoguer" 207 | version = "0.11.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" 210 | dependencies = [ 211 | "console", 212 | "shell-words", 213 | "tempfile", 214 | "thiserror", 215 | "zeroize", 216 | ] 217 | 218 | [[package]] 219 | name = "encode_unicode" 220 | version = "0.3.6" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 223 | 224 | [[package]] 225 | name = "errno" 226 | version = "0.3.9" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 229 | dependencies = [ 230 | "libc", 231 | "windows-sys 0.52.0", 232 | ] 233 | 234 | [[package]] 235 | name = "fastrand" 236 | version = "2.1.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" 239 | 240 | [[package]] 241 | name = "form_urlencoded" 242 | version = "1.2.1" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 245 | dependencies = [ 246 | "percent-encoding", 247 | ] 248 | 249 | [[package]] 250 | name = "getrandom" 251 | version = "0.2.15" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 254 | dependencies = [ 255 | "cfg-if", 256 | "libc", 257 | "wasi", 258 | ] 259 | 260 | [[package]] 261 | name = "git-glance" 262 | version = "0.1.0" 263 | dependencies = [ 264 | "anyhow", 265 | "chrono", 266 | "clap", 267 | "colored", 268 | "console", 269 | "dialoguer", 270 | "git2", 271 | "indicatif", 272 | "openai-api-rs", 273 | "serde", 274 | "serde_json", 275 | ] 276 | 277 | [[package]] 278 | name = "git2" 279 | version = "0.19.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" 282 | dependencies = [ 283 | "bitflags", 284 | "libc", 285 | "libgit2-sys", 286 | "log", 287 | "openssl-probe", 288 | "openssl-sys", 289 | "url", 290 | ] 291 | 292 | [[package]] 293 | name = "heck" 294 | version = "0.5.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 297 | 298 | [[package]] 299 | name = "iana-time-zone" 300 | version = "0.1.60" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 303 | dependencies = [ 304 | "android_system_properties", 305 | "core-foundation-sys", 306 | "iana-time-zone-haiku", 307 | "js-sys", 308 | "wasm-bindgen", 309 | "windows-core", 310 | ] 311 | 312 | [[package]] 313 | name = "iana-time-zone-haiku" 314 | version = "0.1.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 317 | dependencies = [ 318 | "cc", 319 | ] 320 | 321 | [[package]] 322 | name = "idna" 323 | version = "0.5.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 326 | dependencies = [ 327 | "unicode-bidi", 328 | "unicode-normalization", 329 | ] 330 | 331 | [[package]] 332 | name = "indicatif" 333 | version = "0.17.8" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" 336 | dependencies = [ 337 | "console", 338 | "instant", 339 | "number_prefix", 340 | "portable-atomic", 341 | "unicode-width", 342 | ] 343 | 344 | [[package]] 345 | name = "instant" 346 | version = "0.1.13" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 349 | dependencies = [ 350 | "cfg-if", 351 | ] 352 | 353 | [[package]] 354 | name = "is_terminal_polyfill" 355 | version = "1.70.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" 358 | 359 | [[package]] 360 | name = "itoa" 361 | version = "1.0.11" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 364 | 365 | [[package]] 366 | name = "jobserver" 367 | version = "0.1.31" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 370 | dependencies = [ 371 | "libc", 372 | ] 373 | 374 | [[package]] 375 | name = "js-sys" 376 | version = "0.3.69" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 379 | dependencies = [ 380 | "wasm-bindgen", 381 | ] 382 | 383 | [[package]] 384 | name = "lazy_static" 385 | version = "1.5.0" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 388 | 389 | [[package]] 390 | name = "libc" 391 | version = "0.2.155" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 394 | 395 | [[package]] 396 | name = "libgit2-sys" 397 | version = "0.17.0+1.8.1" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" 400 | dependencies = [ 401 | "cc", 402 | "libc", 403 | "libssh2-sys", 404 | "libz-sys", 405 | "openssl-sys", 406 | "pkg-config", 407 | ] 408 | 409 | [[package]] 410 | name = "libssh2-sys" 411 | version = "0.3.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" 414 | dependencies = [ 415 | "cc", 416 | "libc", 417 | "libz-sys", 418 | "openssl-sys", 419 | "pkg-config", 420 | "vcpkg", 421 | ] 422 | 423 | [[package]] 424 | name = "libz-sys" 425 | version = "1.1.18" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" 428 | dependencies = [ 429 | "cc", 430 | "libc", 431 | "pkg-config", 432 | "vcpkg", 433 | ] 434 | 435 | [[package]] 436 | name = "linux-raw-sys" 437 | version = "0.4.14" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 440 | 441 | [[package]] 442 | name = "log" 443 | version = "0.4.22" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 446 | 447 | [[package]] 448 | name = "minreq" 449 | version = "2.11.2" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "6fdef521c74c2884a4f3570bcdb6d2a77b3c533feb6b27ac2ae72673cc221c64" 452 | dependencies = [ 453 | "base64", 454 | "log", 455 | "once_cell", 456 | "rustls", 457 | "rustls-webpki", 458 | "serde", 459 | "serde_json", 460 | "webpki-roots", 461 | ] 462 | 463 | [[package]] 464 | name = "num-traits" 465 | version = "0.2.19" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 468 | dependencies = [ 469 | "autocfg", 470 | ] 471 | 472 | [[package]] 473 | name = "number_prefix" 474 | version = "0.4.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 477 | 478 | [[package]] 479 | name = "once_cell" 480 | version = "1.19.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 483 | 484 | [[package]] 485 | name = "openai-api-rs" 486 | version = "4.1.1" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "bd769889947d8e577296423aa2fcbd739e667d01768486d99e2cfdd9d739d832" 489 | dependencies = [ 490 | "minreq", 491 | "serde", 492 | "serde_json", 493 | ] 494 | 495 | [[package]] 496 | name = "openssl-probe" 497 | version = "0.1.5" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 500 | 501 | [[package]] 502 | name = "openssl-sys" 503 | version = "0.9.102" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" 506 | dependencies = [ 507 | "cc", 508 | "libc", 509 | "pkg-config", 510 | "vcpkg", 511 | ] 512 | 513 | [[package]] 514 | name = "percent-encoding" 515 | version = "2.3.1" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 518 | 519 | [[package]] 520 | name = "pkg-config" 521 | version = "0.3.30" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 524 | 525 | [[package]] 526 | name = "portable-atomic" 527 | version = "1.6.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 530 | 531 | [[package]] 532 | name = "proc-macro2" 533 | version = "1.0.86" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 536 | dependencies = [ 537 | "unicode-ident", 538 | ] 539 | 540 | [[package]] 541 | name = "quote" 542 | version = "1.0.36" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 545 | dependencies = [ 546 | "proc-macro2", 547 | ] 548 | 549 | [[package]] 550 | name = "ring" 551 | version = "0.17.8" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 554 | dependencies = [ 555 | "cc", 556 | "cfg-if", 557 | "getrandom", 558 | "libc", 559 | "spin", 560 | "untrusted", 561 | "windows-sys 0.52.0", 562 | ] 563 | 564 | [[package]] 565 | name = "rustix" 566 | version = "0.38.34" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 569 | dependencies = [ 570 | "bitflags", 571 | "errno", 572 | "libc", 573 | "linux-raw-sys", 574 | "windows-sys 0.52.0", 575 | ] 576 | 577 | [[package]] 578 | name = "rustls" 579 | version = "0.21.12" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" 582 | dependencies = [ 583 | "log", 584 | "ring", 585 | "rustls-webpki", 586 | "sct", 587 | ] 588 | 589 | [[package]] 590 | name = "rustls-webpki" 591 | version = "0.101.7" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" 594 | dependencies = [ 595 | "ring", 596 | "untrusted", 597 | ] 598 | 599 | [[package]] 600 | name = "ryu" 601 | version = "1.0.18" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 604 | 605 | [[package]] 606 | name = "sct" 607 | version = "0.7.1" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" 610 | dependencies = [ 611 | "ring", 612 | "untrusted", 613 | ] 614 | 615 | [[package]] 616 | name = "serde" 617 | version = "1.0.203" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" 620 | dependencies = [ 621 | "serde_derive", 622 | ] 623 | 624 | [[package]] 625 | name = "serde_derive" 626 | version = "1.0.203" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" 629 | dependencies = [ 630 | "proc-macro2", 631 | "quote", 632 | "syn", 633 | ] 634 | 635 | [[package]] 636 | name = "serde_json" 637 | version = "1.0.120" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" 640 | dependencies = [ 641 | "itoa", 642 | "ryu", 643 | "serde", 644 | ] 645 | 646 | [[package]] 647 | name = "shell-words" 648 | version = "1.1.0" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 651 | 652 | [[package]] 653 | name = "spin" 654 | version = "0.9.8" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 657 | 658 | [[package]] 659 | name = "strsim" 660 | version = "0.11.1" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 663 | 664 | [[package]] 665 | name = "syn" 666 | version = "2.0.68" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" 669 | dependencies = [ 670 | "proc-macro2", 671 | "quote", 672 | "unicode-ident", 673 | ] 674 | 675 | [[package]] 676 | name = "tempfile" 677 | version = "3.10.1" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 680 | dependencies = [ 681 | "cfg-if", 682 | "fastrand", 683 | "rustix", 684 | "windows-sys 0.52.0", 685 | ] 686 | 687 | [[package]] 688 | name = "thiserror" 689 | version = "1.0.61" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" 692 | dependencies = [ 693 | "thiserror-impl", 694 | ] 695 | 696 | [[package]] 697 | name = "thiserror-impl" 698 | version = "1.0.61" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" 701 | dependencies = [ 702 | "proc-macro2", 703 | "quote", 704 | "syn", 705 | ] 706 | 707 | [[package]] 708 | name = "tinyvec" 709 | version = "1.6.1" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" 712 | dependencies = [ 713 | "tinyvec_macros", 714 | ] 715 | 716 | [[package]] 717 | name = "tinyvec_macros" 718 | version = "0.1.1" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 721 | 722 | [[package]] 723 | name = "unicode-bidi" 724 | version = "0.3.15" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 727 | 728 | [[package]] 729 | name = "unicode-ident" 730 | version = "1.0.12" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 733 | 734 | [[package]] 735 | name = "unicode-normalization" 736 | version = "0.1.23" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 739 | dependencies = [ 740 | "tinyvec", 741 | ] 742 | 743 | [[package]] 744 | name = "unicode-width" 745 | version = "0.1.13" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 748 | 749 | [[package]] 750 | name = "untrusted" 751 | version = "0.9.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 754 | 755 | [[package]] 756 | name = "url" 757 | version = "2.5.2" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 760 | dependencies = [ 761 | "form_urlencoded", 762 | "idna", 763 | "percent-encoding", 764 | ] 765 | 766 | [[package]] 767 | name = "utf8parse" 768 | version = "0.2.2" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 771 | 772 | [[package]] 773 | name = "vcpkg" 774 | version = "0.2.15" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 777 | 778 | [[package]] 779 | name = "wasi" 780 | version = "0.11.0+wasi-snapshot-preview1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 783 | 784 | [[package]] 785 | name = "wasm-bindgen" 786 | version = "0.2.92" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 789 | dependencies = [ 790 | "cfg-if", 791 | "wasm-bindgen-macro", 792 | ] 793 | 794 | [[package]] 795 | name = "wasm-bindgen-backend" 796 | version = "0.2.92" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 799 | dependencies = [ 800 | "bumpalo", 801 | "log", 802 | "once_cell", 803 | "proc-macro2", 804 | "quote", 805 | "syn", 806 | "wasm-bindgen-shared", 807 | ] 808 | 809 | [[package]] 810 | name = "wasm-bindgen-macro" 811 | version = "0.2.92" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 814 | dependencies = [ 815 | "quote", 816 | "wasm-bindgen-macro-support", 817 | ] 818 | 819 | [[package]] 820 | name = "wasm-bindgen-macro-support" 821 | version = "0.2.92" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 824 | dependencies = [ 825 | "proc-macro2", 826 | "quote", 827 | "syn", 828 | "wasm-bindgen-backend", 829 | "wasm-bindgen-shared", 830 | ] 831 | 832 | [[package]] 833 | name = "wasm-bindgen-shared" 834 | version = "0.2.92" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 837 | 838 | [[package]] 839 | name = "webpki-roots" 840 | version = "0.25.4" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" 843 | 844 | [[package]] 845 | name = "windows-core" 846 | version = "0.52.0" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 849 | dependencies = [ 850 | "windows-targets 0.52.5", 851 | ] 852 | 853 | [[package]] 854 | name = "windows-sys" 855 | version = "0.48.0" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 858 | dependencies = [ 859 | "windows-targets 0.48.5", 860 | ] 861 | 862 | [[package]] 863 | name = "windows-sys" 864 | version = "0.52.0" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 867 | dependencies = [ 868 | "windows-targets 0.52.5", 869 | ] 870 | 871 | [[package]] 872 | name = "windows-targets" 873 | version = "0.48.5" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 876 | dependencies = [ 877 | "windows_aarch64_gnullvm 0.48.5", 878 | "windows_aarch64_msvc 0.48.5", 879 | "windows_i686_gnu 0.48.5", 880 | "windows_i686_msvc 0.48.5", 881 | "windows_x86_64_gnu 0.48.5", 882 | "windows_x86_64_gnullvm 0.48.5", 883 | "windows_x86_64_msvc 0.48.5", 884 | ] 885 | 886 | [[package]] 887 | name = "windows-targets" 888 | version = "0.52.5" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 891 | dependencies = [ 892 | "windows_aarch64_gnullvm 0.52.5", 893 | "windows_aarch64_msvc 0.52.5", 894 | "windows_i686_gnu 0.52.5", 895 | "windows_i686_gnullvm", 896 | "windows_i686_msvc 0.52.5", 897 | "windows_x86_64_gnu 0.52.5", 898 | "windows_x86_64_gnullvm 0.52.5", 899 | "windows_x86_64_msvc 0.52.5", 900 | ] 901 | 902 | [[package]] 903 | name = "windows_aarch64_gnullvm" 904 | version = "0.48.5" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 907 | 908 | [[package]] 909 | name = "windows_aarch64_gnullvm" 910 | version = "0.52.5" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 913 | 914 | [[package]] 915 | name = "windows_aarch64_msvc" 916 | version = "0.48.5" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 919 | 920 | [[package]] 921 | name = "windows_aarch64_msvc" 922 | version = "0.52.5" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 925 | 926 | [[package]] 927 | name = "windows_i686_gnu" 928 | version = "0.48.5" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 931 | 932 | [[package]] 933 | name = "windows_i686_gnu" 934 | version = "0.52.5" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 937 | 938 | [[package]] 939 | name = "windows_i686_gnullvm" 940 | version = "0.52.5" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 943 | 944 | [[package]] 945 | name = "windows_i686_msvc" 946 | version = "0.48.5" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 949 | 950 | [[package]] 951 | name = "windows_i686_msvc" 952 | version = "0.52.5" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 955 | 956 | [[package]] 957 | name = "windows_x86_64_gnu" 958 | version = "0.48.5" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 961 | 962 | [[package]] 963 | name = "windows_x86_64_gnu" 964 | version = "0.52.5" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 967 | 968 | [[package]] 969 | name = "windows_x86_64_gnullvm" 970 | version = "0.48.5" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 973 | 974 | [[package]] 975 | name = "windows_x86_64_gnullvm" 976 | version = "0.52.5" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 979 | 980 | [[package]] 981 | name = "windows_x86_64_msvc" 982 | version = "0.48.5" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 985 | 986 | [[package]] 987 | name = "windows_x86_64_msvc" 988 | version = "0.52.5" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 991 | 992 | [[package]] 993 | name = "zeroize" 994 | version = "1.8.1" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 997 | --------------------------------------------------------------------------------