sbom_ghr/
lib.rs

1use std::{
2    collections::HashMap,
3    io::{Cursor, SeekFrom},
4};
5
6use anyhow::{anyhow, Result};
7use octocrab::Octocrab;
8use url::Url;
9
10use crate::{
11    analyzers::SpdxFileAnalyzeSuccess,
12    packages::{GitPackage, TarPackage, ZipPackage},
13    spdx::SpdxDocument,
14};
15
16mod analyzers;
17mod packages;
18mod spdx;
19
20fn file_name_from_url(url: &Url) -> Result<String> {
21    let segments = url
22        .path_segments()
23        .ok_or_else(|| anyhow!("can not take to path segments from url {}", url))?;
24    let last_segment = segments
25        .last()
26        .ok_or_else(|| anyhow!("can not take a last segment from url : {}", url))?;
27    Ok(last_segment.to_string())
28}
29
30async fn download_file_from_url<W: std::io::Write + std::io::Seek>(
31    url: &Url,
32    file: &mut W,
33) -> Result<()> {
34    let http_client = reqwest::Client::default();
35    let response = http_client.get(url.as_str()).send().await?;
36    let mut content = Cursor::new(response.bytes().await?);
37    std::io::copy(&mut content, file)?;
38    file.seek(SeekFrom::Start(0))?;
39    Ok(())
40}
41
42#[derive(clap::Args, Debug)]
43pub struct DescribeArgs {
44    owner: String,
45    repo: String,
46    tag: String,
47}
48
49type Files = HashMap<String, SpdxFileAnalyzeSuccess>;
50
51impl DescribeArgs {
52    async fn analyze_tar(tar_url: &Url) -> Result<Files> {
53        let mut file = tempfile::tempfile()?;
54        download_file_from_url(tar_url, &mut file).await?;
55        let files_from_tar = TarPackage::from_read(file).analyze_files()?;
56        Ok(files_from_tar)
57    }
58
59    async fn analyze_zip(tar_url: &Url) -> Result<Files> {
60        let mut file = tempfile::tempfile()?;
61        download_file_from_url(tar_url, &mut file).await?;
62        let files_from_tar = ZipPackage::from_read(file)?.analyze_files()?;
63        Ok(files_from_tar)
64    }
65
66    fn analyze_git(clone_url: Url, tag: String) -> Result<Files> {
67        let package = GitPackage::checkout(&clone_url, &tag)?;
68        Ok(package.analyze_files()?)
69    }
70
71    pub async fn run(self) -> Result<()> {
72        let mut spdx_doc = SpdxDocument::new(&format!("{}_{}", self.repo, self.tag));
73        let octocrab = Octocrab::builder().build()?;
74        let repo_client = octocrab.repos(self.owner, self.repo);
75        let repo = repo_client.get().await?;
76        let release = repo_client.releases().get_by_tag(&self.tag).await?;
77        println!("procesing release : {:?}", release);
78        let git_analyze_task = {
79            let clone_url = repo.clone_url.unwrap().clone();
80            tokio::spawn(async move { Self::analyze_git(clone_url, self.tag) })
81        };
82        let tar_analyze_task = if let Some(ref tar_url) = release.tarball_url {
83            let tar_url = tar_url.clone();
84            Some(tokio::spawn(
85                async move { Self::analyze_tar(&tar_url).await },
86            ))
87        } else {
88            None
89        };
90        let zip_analyze_task = if let Some(ref zip_url) = release.zipball_url {
91            let zip_url = zip_url.clone();
92            Some(tokio::spawn(
93                async move { Self::analyze_zip(&zip_url).await },
94            ))
95        } else {
96            None
97        };
98
99        let git_result = git_analyze_task.await?;
100
101        let zip_result = if let Some(zip_task) = zip_analyze_task {
102            Some(zip_task.await?)
103        } else {
104            None
105        };
106        let tar_result = if let Some(tar_task) = tar_analyze_task {
107            Some(tar_task.await?)
108        } else {
109            None
110        };
111
112        for asset in release.assets {
113            println!("processing asset : {:?}", asset);
114        }
115
116        Ok(())
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    #[test]
123    fn it_works() {
124        let result = 2 + 2;
125        assert_eq!(result, 4);
126    }
127}