use super::{util, XCCompileArgs};
use crate::parser::XCCompileCommandData;
use anyhow::Result;
use lazy_regex::regex_captures as cap;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct XCCompileCommand {
#[serde(rename(serialize = "module_name"))]
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file: Option<PathBuf>,
pub directory: String,
pub command: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub files: Option<Vec<PathBuf>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub file_lists: Vec<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output: Option<String>,
#[serde(skip)]
pub index_store_path: Option<PathBuf>,
}
impl XCCompileCommand {
pub fn from_compile_command_data(data: XCCompileCommandData) -> Option<Self> {
if !(data.name.contains("swiftc") || data.name.contains("clang")) {
return None;
}
let is_clang = data.name.contains("clang");
let args = &data.arguments;
let mut command = Self::default();
command.command = format!("{} {args}", data.command);
if is_clang {
if let Some(file) = cap!(r"-c\s(.*)-o", args).map(|(_, c)| c.trim()) {
command.file = Some(file.into());
} else {
return None;
}
} else {
command.name = cap!(r"-module-name\s(\w+)\s", args).map(|(_, c)| c.into());
if let Some(file_lists) = cap!(r"@(/.*.SwiftFileList)", args).map(|(_, c)| c) {
command.file_lists = vec![file_lists.replace("\\", "").into()];
};
}
command.index_store_path =
cap!(r"-index-store-path\s(/[^\s]+)", args).map(|(_, c)| c.into());
command.directory = cap!(r"-working-directory\s(.*)\s*", args)
.map(|(_, c)| c.to_string())
.unwrap_or_else(|| "/".to_string());
Some(command)
}
pub fn compile_flags<'a>(&'a self) -> Result<HashMap<PathBuf, XCCompileArgs>> {
let mut info = HashMap::default();
let flags = XCCompileArgs::try_from_str(&self.command)?;
self.file_lists.iter().for_each(|path| {
match util::get_files_list(&path) {
Ok(file_list) => {
file_list.into_iter().for_each(|file_path: PathBuf| {
info.insert(file_path, flags.clone());
});
}
Err(e) => tracing::error!("Fail to get file lists {e}"),
};
});
self.files.as_ref().map(|files| {
files.iter().for_each(|file| {
info.insert(file.clone(), flags.clone());
})
});
self.file
.as_ref()
.map(|file| info.insert(file.clone(), flags.clone()));
Ok(info)
}
}