1use alloy_primitives::Address;
8use path_slash::PathExt;
9use serde::Deserialize;
10use std::{
11 fs,
12 path::{Path, PathBuf},
13};
14
15pub fn git_source_url(repo: &str, commit: &str, root: &Path, item_path: &Path) -> Option<String> {
26 let repo = repo.trim_end_matches('/');
27 let commit = if commit.is_empty() { "HEAD" } else { commit };
28 let rel = item_path.strip_prefix(root).ok()?;
29 Some(format!("{repo}/blob/{commit}/{}", rel.to_slash_lossy()))
30}
31
32pub fn git_raw_url(repo: &str, commit: &str, root: &Path, item_path: &Path) -> Option<String> {
35 let repo = repo.trim_end_matches('/');
36 let commit = if commit.is_empty() { "HEAD" } else { commit };
37 let rel = item_path.strip_prefix(root).ok()?;
38 Some(format!("{repo}/raw/{commit}/{}", rel.to_slash_lossy()))
39}
40
41#[derive(Clone, Debug, Deserialize)]
45pub struct Deployment {
46 pub address: Address,
48 pub network: Option<String>,
50}
51
52pub fn read_deployments(
61 root: &Path,
62 deployments_dir: Option<&Path>,
63 contract_file: &Path,
64) -> Vec<Deployment> {
65 let dir = root.join(deployments_dir.unwrap_or_else(|| Path::new("deployments")));
66 let Ok(entries) = fs::read_dir(&dir) else {
67 return Vec::new();
68 };
69
70 let mut filename: PathBuf = contract_file.to_path_buf();
72 filename.set_extension("json");
73 let Some(filename) = filename.file_name().map(PathBuf::from) else { return Vec::new() };
74
75 let mut out = Vec::new();
76 for entry in entries.flatten() {
77 let Ok(file_type) = entry.file_type() else { continue };
78 if !file_type.is_dir() {
79 continue;
80 }
81 let Ok(network) = entry.file_name().into_string() else { continue };
82 let path = entry.path().join(&filename);
83 let Ok(content) = fs::read_to_string(&path) else { continue };
84 let Ok(mut deployment) = serde_json::from_str::<Deployment>(&content) else { continue };
85 deployment.network = Some(network);
86 out.push(deployment);
87 }
88 out.sort_by(|a, b| {
90 a.network.as_deref().unwrap_or("").cmp(b.network.as_deref().unwrap_or("")).then_with(|| {
91 let af = format!("{:#x}", a.address);
92 let bf = format!("{:#x}", b.address);
93 af.cmp(&bf)
94 })
95 });
96 out
97}