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
use crate::{
    data::build_plan::BuildPlan,
    platform::{GenericPlatform, Platform},
    shared::write_toml_file,
};
use std::{
    env,
    path::{Path, PathBuf},
    process,
};

pub fn cnb_runtime_detect<
    P: Platform,
    E: std::fmt::Display,
    F: FnOnce(DetectContext<P>) -> Result<DetectOutcome, E>,
>(
    detect_fn: F,
) {
    let app_dir = env::current_dir().expect("Could not determine current working directory!");

    let buildpack_dir = env::var("CNB_BUILDPACK_DIR")
        .expect("Could not determine buildpack directory!")
        .into();

    let args: Vec<String> = env::args().collect();
    if args.len() != 3 {
        eprintln!("Usage: detect <platform_dir> <buildplan>");
        process::exit(1);
    }

    let platform = {
        let platform_dir = PathBuf::from(args.get(1).unwrap());

        if !platform_dir.is_dir() {
            eprintln!("First argument must be a readable platform directory!");
            process::exit(1);
        }

        match P::from_path(platform_dir.as_path()) {
            Ok(platform) => platform,
            Err(error) => {
                eprintln!(
                    "Could not create platform from platform directory: {}",
                    error
                );
                process::exit(1);
            }
        }
    };

    let build_plan_path: PathBuf = PathBuf::from(args.get(2).unwrap());

    let detect_context = DetectContext {
        app_dir,
        buildpack_dir,
        platform,
    };

    match detect_fn(detect_context) {
        Ok(DetectOutcome::Fail) | Err(_) => process::exit(100),
        Ok(DetectOutcome::Error(code)) => process::exit(code),
        Ok(DetectOutcome::Pass(build_plan)) => {
            if let Err(error) = write_toml_file(&build_plan, build_plan_path) {
                eprintln!("Could not write buildplan to disk: {}", error);
                process::exit(1);
            }

            process::exit(0)
        }
    };
}

pub struct DetectContext<P: Platform> {
    app_dir: PathBuf,
    buildpack_dir: PathBuf,
    pub platform: P,
}

impl<P: Platform> DetectContext<P> {
    pub fn app_dir(&self) -> &Path {
        self.app_dir.as_path()
    }

    pub fn buildpack_dir(&self) -> &Path {
        self.buildpack_dir.as_path()
    }
}

pub type GenericDetectContext = DetectContext<GenericPlatform>;

#[derive(Debug)]
pub enum DetectOutcome {
    Pass(BuildPlan),
    Fail,
    Error(i32),
}