libmake/utils.rs
1// Copyright notice and licensing information.
2// These lines indicate the copyright of the software and its licensing terms.
3// SPDX-License-Identifier: Apache-2.0 OR MIT indicates dual licensing under Apache 2.0 or MIT licenses.
4// Copyright © 2023-2024 LibMake. All rights reserved.
5
6use crate::macro_get_field;
7use std::{
8 env,
9 fs::{self, File},
10 io,
11 path::Path,
12};
13
14/// Reads a file and deserializes its content using the specified deserializer function.
15///
16/// # Arguments
17///
18/// * `file_path` - The path of the file to read.
19/// * `deserializer` - A function that takes a `File` and returns a deserialized value of type `T`.
20///
21/// # Returns
22///
23/// Returns a `Result<T, Box<dyn std::error::Error>>` containing the deserialized value, or an error if one occurs.
24///
25fn read_file<T, F>(
26 file_path: &Path,
27 deserializer: F,
28) -> Result<T, Box<dyn std::error::Error>>
29where
30 F: FnOnce(File) -> Result<T, Box<dyn std::error::Error>>,
31{
32 let file = File::open(file_path)?;
33 deserializer(file)
34}
35
36/// Reads a CSV file at the given file path and returns the value of
37/// the given field.
38///
39/// # Arguments
40///
41/// * `file_path` - An optional string slice that holds the file path of the CSV file to read.
42/// * `field_index` - The index of the field to retrieve.
43///
44pub fn get_csv_field(
45 file_path: Option<&str>,
46 field_index: usize,
47) -> Option<Vec<String>> {
48 file_path.and_then(|file_path| {
49 let current_dir = env::current_dir().ok()?;
50 let file_path = Path::new(¤t_dir).join(file_path);
51 let file = File::open(file_path).ok()?;
52 let mut rdr = csv::Reader::from_reader(file);
53
54 let mut values = Vec::new();
55 for result in rdr.records() {
56 let record = result.ok()?;
57 if let Some(field_value) = record.get(field_index) {
58 values.push(field_value.to_string());
59 } else {
60 // Field index is out of range
61 return None;
62 }
63 }
64 if values.is_empty() {
65 None
66 } else {
67 Some(values)
68 }
69 })
70}
71
72macro_get_field!(get_ini_field, serde_ini::from_read);
73macro_get_field!(get_json_field, serde_json::from_reader);
74macro_get_field!(get_yaml_field, serde_yml::from_reader);
75
76/// Retrieves a specific field's value from a configuration file.
77///
78/// # Arguments
79///
80/// * `file_path` - An optional reference to the path of the configuration file.
81/// * `file_format` - The format of the configuration file ("json", "yaml", or "ini").
82/// * `field_name` - The name of the field to retrieve the value from.
83///
84/// # Returns
85///
86/// Returns a `Result<String, Box<dyn std::error::Error>>` containing the value of the specified field, or an error if one occurs.
87///
88pub fn get_config_field(
89 file_path: Option<&str>,
90 file_format: Option<&str>,
91 field_name: &str,
92) -> Result<String, Box<dyn std::error::Error>> {
93 // Ensure file_path is provided
94 let file_path = file_path.ok_or("File path is not provided")?;
95
96 // Ensure file_format is provided and is either 'json', 'yaml', or 'ini'
97 let format = file_format.ok_or("File format is not provided")?;
98 match format {
99 "ini" => get_ini_field(Some(file_path), field_name),
100 "json" => get_json_field(Some(file_path), field_name),
101 "yaml" => get_yaml_field(Some(file_path), field_name),
102 _ => Err(format!(
103 "Unsupported file format: {}. Supported formats are 'json', 'yaml', and 'ini'.",
104 format
105 )
106 .into()),
107 }
108}
109
110/// Creates a directory at the specified path.
111///
112/// # Arguments
113///
114/// * `path` - The path where the directory should be created.
115///
116/// # Errors
117///
118/// Returns an `io::Error` if the directory cannot be created. This could be due to
119/// various reasons such as insufficient permissions, the directory already existing,
120/// or other I/O-related errors.
121///
122pub fn create_directory(path: &Path) -> io::Result<()> {
123 fs::create_dir(path).or_else(|e| match e.kind() {
124 io::ErrorKind::AlreadyExists => Ok(()),
125 _ => Err(e),
126 })
127}