use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;
use whisker_plugin::{FileEntry, GenerateContext, Operation, Plugin, PluginConfig, Target};
#[derive(Default, Serialize, Deserialize)]
pub struct IosExtraFilesConfig {
#[serde(default)]
pub files: BTreeMap<PathBuf, FileEntry>,
}
impl IosExtraFilesConfig {
pub fn add(&mut self, path: impl Into<PathBuf>, contents: impl Into<String>) -> &mut Self {
self.files.insert(
path.into(),
FileEntry {
contents: contents.into(),
contents_base64: None,
mode: None,
},
);
self
}
pub fn add_with_mode(
&mut self,
path: impl Into<PathBuf>,
contents: impl Into<String>,
mode: u32,
) -> &mut Self {
self.files.insert(
path.into(),
FileEntry {
contents: contents.into(),
contents_base64: None,
mode: Some(mode),
},
);
self
}
}
impl PluginConfig for IosExtraFilesConfig {
const NAME: &'static str = "whisker-ios-extra-files";
}
pub struct IosExtraFiles;
impl Plugin for IosExtraFiles {
type Config = IosExtraFilesConfig;
fn apply(&self, ctx: &mut GenerateContext, cfg: &IosExtraFilesConfig) -> anyhow::Result<()> {
let Some(ios) = ctx.ios.as_mut() else {
return Ok(());
};
if cfg.files.is_empty() {
return Ok(());
}
let count = cfg.files.len();
for (path, entry) in &cfg.files {
ios.extra_files.insert(path.clone(), entry.clone());
}
ctx.journal.record(
IosExtraFilesConfig::NAME,
Target::Ios,
"extra_files",
Operation::ArrayPush { count },
);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use whisker_plugin::IosProjectIr;
fn ctx_with_ios() -> GenerateContext {
GenerateContext {
ios: Some(IosProjectIr::default()),
..Default::default()
}
}
#[test]
fn default_config_contributes_nothing() {
let mut ctx = ctx_with_ios();
IosExtraFiles
.apply(&mut ctx, &IosExtraFilesConfig::default())
.unwrap();
assert!(ctx.ios.unwrap().extra_files.is_empty());
assert!(ctx.journal.records.is_empty());
}
#[test]
fn populated_config_writes_each_file_to_ir() {
let mut cfg = IosExtraFilesConfig::default();
cfg.add("Sources/Helper.swift", "// helper").add_with_mode(
"Scripts/run.sh",
"#!/bin/sh\necho ok\n",
0o755,
);
let mut ctx = ctx_with_ios();
IosExtraFiles.apply(&mut ctx, &cfg).unwrap();
let files = ctx.ios.unwrap().extra_files;
assert_eq!(files.len(), 2);
let helper = &files[&PathBuf::from("Sources/Helper.swift")];
assert_eq!(helper.contents, "// helper");
assert!(helper.mode.is_none());
let script = &files[&PathBuf::from("Scripts/run.sh")];
assert_eq!(script.mode, Some(0o755));
}
#[test]
fn one_array_push_event_per_invocation() {
let mut cfg = IosExtraFilesConfig::default();
cfg.add("a.swift", "").add("b.swift", "").add("c.swift", "");
let mut ctx = ctx_with_ios();
IosExtraFiles.apply(&mut ctx, &cfg).unwrap();
assert_eq!(ctx.journal.records.len(), 1);
let r = &ctx.journal.records[0];
assert_eq!(r.plugin, "whisker-ios-extra-files");
assert!(matches!(r.operation, Operation::ArrayPush { count: 3 }));
}
#[test]
fn no_ios_target_means_no_op() {
let mut cfg = IosExtraFilesConfig::default();
cfg.add("a.swift", "");
let mut ctx = GenerateContext::default();
IosExtraFiles.apply(&mut ctx, &cfg).unwrap();
assert!(ctx.journal.records.is_empty());
}
}