summaryrefslogtreecommitdiffstats
path: root/src/commands/find_artifact.rs
blob: ee11229b802e2f64ad35ecd5e3525c7d9328b675 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//
// Copyright (c) 2020-2021 science+computing ag and other contributors
//
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//

use std::path::PathBuf;
use std::io::Write;
use std::sync::Arc;

use anyhow::Context;
use anyhow::Error;
use anyhow::Result;
use clap::ArgMatches;
use diesel::PgConnection;
use itertools::Itertools;
use log::debug;
use log::trace;

use crate::config::Configuration;
use crate::filestore::ReleaseStore;
use crate::filestore::StagingStore;
use crate::filestore::path::StoreRoot;
use crate::package::PackageVersionConstraint;
use crate::repository::Repository;
use crate::util::progress::ProgressBars;

/// Implementation of the "find_artifact" subcommand
pub async fn find_artifact(matches: &ArgMatches, config: &Configuration, progressbars: ProgressBars, repo: Repository, database_connection: PgConnection) -> Result<()> {
    let package_name_regex = crate::commands::util::mk_package_name_regex({
        matches.value_of("package_name_regex").unwrap() // safe by clap
    })?;

    let package_version_constraint = matches
        .value_of("package_version_constraint")
        .map(String::from)
        .map(PackageVersionConstraint::new)
        .transpose()
        .context("Parsing package version constraint")
        .context("A valid package version constraint looks like this: '=1.0.0'")?;

    let env_filter = matches.values_of("env_filter")
        .map(|vals| vals.map(crate::util::env::parse_to_env).collect::<Result<Vec<_>>>())
        .transpose()?
        .unwrap_or_default();

    log::debug!("Finding artifacts for '{:?}' '{:?}'", package_name_regex, package_version_constraint);

    let release_stores = config
        .release_stores()
        .iter()
        .map(|storename| {
            let bar_release_loading = progressbars.bar();

            let p = config.releases_directory().join(storename);
            debug!("Loading release directory: {}", p.display());
            let r = ReleaseStore::load(StoreRoot::new(p)?, &bar_release_loading);
            if r.is_ok() {
                bar_release_loading.finish_with_message("Loaded releases successfully");
            } else {
                bar_release_loading.finish_with_message("Failed to load releases");
            }

            r.map(Arc::new)
        })
        .collect::<Result<Vec<_>>>()?;

    let staging_store = if let Some(p) = matches.value_of("staging_dir").map(PathBuf::from) {
        let bar_staging_loading = progressbars.bar();

        if !p.is_dir() {
            let _ = tokio::fs::create_dir_all(&p).await?;
        }

        debug!("Loading staging directory: {}", p.display());
        let r = StagingStore::load(StoreRoot::new(p.clone())?, &bar_staging_loading);
        if r.is_ok() {
            bar_staging_loading.finish_with_message("Loaded staging successfully");
        } else {
            bar_staging_loading.finish_with_message("Failed to load staging");
        }
        Some(r?)
    } else {
        None
    };

    let database = Arc::new(database_connection);
    repo.packages()
        .filter(|p| package_name_regex.captures(p.name()).is_some())
        .filter(|p| {
            package_version_constraint
                .as_ref()
                .map(|v| v.matches(p.version()))
                .unwrap_or(true)
        })
        .inspect(|pkg| trace!("Found package: {:?}", pkg))
        .map(|pkg| {
            let script_filter = !matches.is_present("no_script_filter");
            let pathes = crate::db::find_artifacts(database.clone(), config, &pkg, &release_stores, staging_store.as_ref(), &env_filter, script_filter)?;

            pathes.iter()
                .map(|tpl| (tpl.0.joined(), tpl.1))
                .sorted_by(|tpla, tplb| {
                    use std::cmp::Ordering;

                    // Sort the iterator elements, so that if there is a release date, we always
                    // prefer the entry with the release date AS LONG AS the path is equal.
                    match (tpla, tplb) {
                        ((a, Some(ta)), (b, Some(tb))) => match a.cmp(b) {
                            Ordering::Equal => ta.cmp(tb),
                            other => other,
                        },

                        ((a, Some(_)), (b, None)) => match a.cmp(b) {
                            Ordering::Equal => Ordering::Greater,
                            other => other,
                        },
                        ((a, None), (b, Some(_))) => match a.cmp(b) {
                            Ordering::Equal => Ordering::Less,
                            other => other,
                        },
                        ((a, None), (b, None)) => a.cmp(b),
                    }
                })
                .unique_by(|tpl| tpl.0.clone()) // TODO: Dont clone()
                .try_for_each(|(path, releasetime)| {
                    if let Some(time) = releasetime {
                        writeln!(std::io::stdout(), "[{}] {}", time, path.display())
                    } else {
                        writeln!(std::io::stdout(), "[unknown] {}", path.display())
                    }.map_err(Error::from)
                })
        })
        .inspect(|r| trace!("Query resulted in: {:?}", r))
        .collect::<Vec<Result<()>>>()
        .into_iter()
        .collect()
}