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
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::fmt::Display;

use anyhow::Result;
use nimbus_fml::{
    intermediate_representation::FeatureManifest, parser::Parser, util::loaders::FileLoader,
};

use crate::{cli::ManifestArgs, config, NimbusApp};

#[derive(Debug, PartialEq)]
pub(crate) enum ManifestSource {
    FromGithub {
        channel: String,

        github_repo: String,
        ref_: String,

        manifest_file: String,
    },
    FromFile {
        channel: String,
        manifest_file: String,
    },
}

impl ManifestSource {
    fn manifest_file(&self) -> &str {
        let (Self::FromFile { manifest_file, .. } | Self::FromGithub { manifest_file, .. }) = self;
        manifest_file
    }

    fn channel(&self) -> &str {
        let (Self::FromFile { channel, .. } | Self::FromGithub { channel, .. }) = self;
        channel
    }

    fn manifest_loader(&self) -> Result<FileLoader> {
        let cwd = std::env::current_dir().expect("Current Working Directory is not set");
        let mut files = FileLoader::new(cwd, config::manifest_cache_dir(), Default::default())?;
        if let Self::FromGithub {
            ref_, github_repo, ..
        } = self
        {
            files.add_repo(github_repo, ref_)?;
        }
        Ok(files)
    }

    pub(crate) fn try_from(params: &NimbusApp, value: &ManifestArgs) -> Result<Self> {
        Ok(
            match (value.manifest.clone(), params.channel(), params.app_name()) {
                (Some(manifest_file), Some(channel), _) => Self::FromFile {
                    channel,
                    manifest_file,
                },
                (_, Some(channel), Some(_)) => {
                    let github_repo = params.github_repo(&value.version)?.to_string();
                    let ref_ = params.ref_from_version(&value.version, &value.ref_)?;
                    let manifest_file = format!(
                        "@{}/{}",
                        github_repo,
                        params.manifest_location(&value.version)?,
                    );
                    Self::FromGithub {
                        channel,
                        manifest_file,
                        ref_,
                        github_repo,
                    }
                }
                _ => anyhow::bail!("A channel and either a manifest or an app is expected"),
            },
        )
    }
}

impl Display for ManifestSource {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let files = self.manifest_loader().unwrap();
        let path = files.file_path(self.manifest_file()).unwrap();
        f.write_str(&path.to_string())
    }
}

impl TryFrom<&ManifestSource> for FeatureManifest {
    type Error = anyhow::Error;

    fn try_from(value: &ManifestSource) -> Result<Self> {
        let files = value.manifest_loader()?;
        let path = files.file_path(value.manifest_file())?;
        let parser: Parser = Parser::new(files, path)?;
        let manifest = parser.get_intermediate_representation(Some(value.channel()))?;
        manifest.validate_manifest()?;
        Ok(manifest)
    }
}