skootrs_lib/service/
output.rs1#![allow(clippy::module_name_repetitions)]
17
18use octocrab::models::repos::{Asset, Release};
19use skootrs_model::skootrs::{
20 label::Label, ProjectOutput, ProjectOutputGetParams, ProjectOutputReference, ProjectOutputType,
21 ProjectOutputsListParams, SkootError,
22};
23pub trait OutputService {
24 fn list(
25 &self,
26 params: ProjectOutputsListParams,
27 ) -> impl std::future::Future<Output = Result<Vec<ProjectOutputReference>, SkootError>> + Send;
28
29 fn get(
30 &self,
31 _params: ProjectOutputGetParams,
32 ) -> impl std::future::Future<Output = Result<ProjectOutput, SkootError>> + Send;
33}
34
35pub struct LocalOutputService;
36
37impl OutputService for LocalOutputService {
38 fn list(
39 &self,
40 params: ProjectOutputsListParams,
41 ) -> impl std::future::Future<Output = Result<Vec<ProjectOutputReference>, SkootError>> + Send
42 {
43 match params.initialized_project.repo {
44 skootrs_model::skootrs::InitializedRepo::Github(g) => {
45 let github_params = GithubReleaseParams {
46 owner: g.organization.get_name(),
47 repo: g.name,
48 tag: params.release.tag(),
49 };
50 GithubReleaseHandler::outputs_list(github_params)
51 }
52 }
53 }
54
55 async fn get(&self, params: ProjectOutputGetParams) -> Result<ProjectOutput, SkootError> {
56 match params.initialized_project.repo {
57 skootrs_model::skootrs::InitializedRepo::Github(g) => {
58 let github_params = GithubOutputGetParams {
59 release: GithubReleaseHandler::get_release(GithubReleaseParams {
60 owner: g.organization.get_name(),
61 repo: g.name.clone(),
62 tag: params.release.tag(),
63 })
64 .await?,
65 name: params.project_output,
66 };
67 GithubReleaseHandler::get_output(github_params).await
68 }
69 }
70 }
71}
72
73struct GithubReleaseHandler;
74impl GithubReleaseHandler {
75 async fn outputs_list(
76 params: GithubReleaseParams,
77 ) -> Result<Vec<ProjectOutputReference>, SkootError> {
78 let release = Self::get_release(params).await?;
79
80 let assets = release.assets;
81 let references = assets
82 .iter()
83 .map(|asset| ProjectOutputReference {
84 name: asset.name.clone(),
85 output_type: Self::get_type(asset),
86 labels: Self::get_labels(asset),
87 })
88 .collect();
89
90 Ok(references)
91 }
92
93 async fn get_release(params: GithubReleaseParams) -> Result<Release, octocrab::Error> {
94 match params.tag {
95 Some(tag) => {
96 octocrab::instance()
97 .repos(params.owner, params.repo)
98 .releases()
99 .get_by_tag(tag.as_str())
100 .await
101 }
102 None => {
103 octocrab::instance()
104 .repos(params.owner, params.repo)
105 .releases()
106 .get_latest()
107 .await
108 }
109 }
110 }
111
112 fn get_type(asset: &Asset) -> ProjectOutputType {
113 match asset.url {
115 _ if asset.name.contains(".spdx.") => ProjectOutputType::SBOM,
117 _ if asset.name.contains(".cdx.") => ProjectOutputType::SBOM,
118 _ if asset.name.contains(".intoto.") => ProjectOutputType::InToto,
119 _ => ProjectOutputType::Unknown("Unknown".to_string()),
121 }
122 }
123
124 fn get_labels(asset: &Asset) -> Vec<Label> {
125 match asset.url {
126 _ if asset.name.contains(".spdx.") => vec![Label::S2C2FAUD4],
127 _ if asset.name.contains(".cdx.") => vec![Label::S2C2FAUD4],
128 _ if asset.name.contains(".intoto.") => vec![Label::SLSABuildLevel3],
129 _ => vec![],
130 }
131 }
132
133 async fn get_output(params: GithubOutputGetParams) -> Result<ProjectOutput, SkootError> {
134 let asset = params
135 .release
136 .assets
137 .iter()
138 .find(|a| a.name == params.name)
139 .ok_or("Asset not found".to_string())?;
140
141 let content = reqwest::get(asset.browser_download_url.clone())
143 .await
144 .map_err(|e| e.to_string())?
145 .text()
146 .await?;
147
148 Ok(ProjectOutput {
149 reference: ProjectOutputReference {
150 name: asset.name.clone(),
151 output_type: Self::get_type(asset),
152 labels: Self::get_labels(asset),
153 },
154 output: serde_json::to_string_pretty(&content)?,
155 })
156 }
157}
158
159struct GithubReleaseParams {
160 owner: String,
161 repo: String,
162 tag: Option<String>,
163}
164
165struct GithubOutputGetParams {
166 release: Release,
167 name: String,
168}