1use anyhow::{bail, Result};
2use clap::{arg, command, value_parser, ArgAction, ArgMatches, Command};
3use schematic::{derive_enum, Config, ConfigEnum};
4use serde::{Deserialize, Serialize};
5use std::{env, path::PathBuf};
6
7use crate::logging::{LogLevel, Logger};
8
9derive_enum!(
10 #[derive(ConfigEnum, Default, Copy)]
11 pub enum SortDirection {
12 #[default]
13 Asc,
14 Desc,
15 }
16);
17
18#[derive(ConfigEnum, Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
19#[serde(rename_all = "snake_case")]
20pub enum OutputElement {
21 Data,
22 FlatData,
23 Content,
24 ContentAst,
25}
26
27#[derive(ConfigEnum, Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
28#[serde(rename_all = "snake_case")]
29pub enum OutputMethod {
30 Single,
31 List,
32 Aggregate,
33}
34
35pub fn get_cli_matches() -> ArgMatches {
36 command!()
37 .arg(
38 arg!(
39 -s --source <DIR> "The location of your source files"
40 )
41 .required(false)
42 .value_parser(value_parser!(PathBuf)),
43 )
44 .arg(
45 arg!(
46 -d --dest <DIR> "The location Flatlake should write your output files. Defaults to `api`"
47 )
48 .required(false)
49 .value_parser(value_parser!(PathBuf)),
50 )
51 .arg(
52 arg!(
53 -v --verbose ... "Print verbose logging while generating files. Does not affect the contents of the output files"
54 )
55 .action(clap::ArgAction::SetTrue),
56 )
57 .arg(
58 arg!(
59 --logfile <DIR> "Path to a logfile to write to. Will replace the file on each run"
60 )
61 .required(false)
62 .value_parser(value_parser!(PathBuf)),
63 )
64 .get_matches()
65}
66
67#[derive(Config, Debug, Clone)]
68#[config(rename_all = "snake_case")]
69pub struct LakeParameters {
70 #[setting(env = "FLATLAKE_SOURCE")]
72 pub source: PathBuf,
73
74 #[setting(default = "api", env = "FLATLAKE_DEST")]
76 pub dest: PathBuf,
77
78 #[setting(env = "FLATLAKE_VERBOSE")]
80 pub verbose: bool,
81
82 #[setting(env = "FLATLAKE_LOGFILE")]
84 pub logfile: Option<PathBuf>,
85
86 #[setting(nested)]
87 pub collections: Vec<LakeCollection>,
88
89 #[setting(nested)]
90 pub global: GlobalLakeSettings,
91}
92
93#[derive(Config, Debug, Clone)]
94#[config(rename_all = "snake_case")]
95pub struct LakeCollection {
96 pub output_key: String,
97 #[setting(nested)]
98 pub inputs: Vec<LakeCollectionInput>,
99 #[setting(default = vec![OutputElement::Data, OutputElement::Content])]
100 pub single_elements: Vec<OutputElement>,
101 #[setting(default = vec![OutputElement::Data])]
102 pub list_elements: Vec<OutputElement>,
103 pub outputs: Option<Vec<OutputMethod>>,
104 pub sort_key: Option<String>,
105 pub sort_direction: Option<SortDirection>,
106 pub page_size: Option<usize>,
107}
108
109#[derive(Config, Debug, Clone)]
110#[config(rename_all = "snake_case")]
111pub struct LakeCollectionInput {
112 pub path: String,
113 #[setting(default = "**/*.{md}")]
114 pub glob: String,
115 pub sub_key: Option<String>,
116 pub merge_data: Option<serde_json::Map<String, serde_json::Value>>,
117}
118
119#[derive(Config, Debug, Clone)]
120#[config(rename_all = "snake_case")]
121pub struct GlobalLakeSettings {
122 #[setting(default = "date")]
123 pub sort_key: String,
124 #[setting(default = "asc")]
125 pub sort_direction: SortDirection,
126 #[setting(default = 100)]
127 pub page_size: usize,
128 #[setting(default = vec![OutputMethod::Single, OutputMethod::List, OutputMethod::Aggregate])]
129 pub outputs: Vec<OutputMethod>,
130}
131
132#[derive(Debug, Clone)]
134pub struct LakeContext {
135 pub version: &'static str,
136 pub logger: Logger,
137 pub working_directory: PathBuf,
138 pub params: LakeParameters,
139}
140
141impl LakeContext {
142 pub fn load(mut config: LakeParameters) -> Result<Self> {
143 let log_level = if config.verbose {
144 LogLevel::Verbose
145 } else {
146 LogLevel::Standard
147 };
148
149 let working_directory = env::current_dir().unwrap();
150
151 config.source = working_directory.join(PathBuf::from(config.source.clone()));
152 config.dest = working_directory.join(PathBuf::from(config.dest.clone()));
153
154 Ok(Self {
155 working_directory,
156 version: env!("CARGO_PKG_VERSION"),
157 logger: Logger::new(log_level, true, config.logfile.clone().map(PathBuf::from)),
158 params: config,
159 })
160 }
161}
162
163impl LakeParameters {
164 pub fn override_from_cli(&mut self, cli_matches: ArgMatches) {
165 if cli_matches.get_flag("verbose") {
166 self.verbose = true;
167 }
168
169 if let Some(source) = cli_matches.get_one::<PathBuf>("source") {
170 self.source = source.clone();
171 }
172
173 if let Some(dest) = cli_matches.get_one::<PathBuf>("dest") {
174 self.dest = dest.clone();
175 }
176
177 if let Some(logfile) = cli_matches.get_one::<PathBuf>("logfile") {
178 self.logfile = Some(logfile.clone());
179 }
180 }
181}