use std::collections::HashMap;
use std::io::Write;
use anyhow::{Context, Result};
#[derive(Debug, Clone)]
pub struct Manifest {
main_attributes: HashMap<String, String>,
sections: HashMap<String, HashMap<String, String>>,
}
impl Manifest {
pub fn new() -> Self {
Self {
main_attributes: HashMap::new(),
sections: HashMap::new(),
}
}
pub fn set_main_attribute(&mut self, key: String, value: String) {
self.main_attributes.insert(key, value);
}
pub fn get_main_attribute(&self, key: &str) -> Option<&String> {
self.main_attributes.get(key)
}
pub fn set_section_attribute(&mut self, section: String, key: String, value: String) {
self.sections
.entry(section)
.or_default()
.insert(key, value);
}
pub fn default_jar_manifest(_group_id: &str, _artifact_id: &str, _version: &str) -> Self {
let mut manifest = Self::new();
manifest.set_main_attribute("Manifest-Version".to_string(), "1.0".to_string());
manifest.set_main_attribute(
"Created-By".to_string(),
format!("jbuild {}", env!("CARGO_PKG_VERSION")),
);
manifest
}
pub fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
for (key, value) in &self.main_attributes {
writeln!(writer, "{key}: {value}")
.context("Failed to write manifest attribute")?;
}
for (section_name, attributes) in &self.sections {
writeln!(writer).context("Failed to write manifest newline")?;
writeln!(writer, "Name: {section_name}")
.context("Failed to write manifest section name")?;
for (key, value) in attributes {
writeln!(writer, "{key}: {value}")
.context("Failed to write manifest section attribute")?;
}
}
writeln!(writer).context("Failed to write manifest final newline")?;
Ok(())
}
pub fn to_bytes(&self) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
self.write_to(&mut buffer)?;
Ok(buffer)
}
}
impl Default for Manifest {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_manifest_new() {
let manifest = Manifest::new();
assert!(manifest.get_main_attribute("Manifest-Version").is_none());
}
#[test]
fn test_manifest_set_main_attribute() {
let mut manifest = Manifest::new();
manifest.set_main_attribute("Manifest-Version".to_string(), "1.0".to_string());
assert_eq!(
manifest.get_main_attribute("Manifest-Version"),
Some(&"1.0".to_string())
);
}
#[test]
fn test_manifest_set_section_attribute() {
let mut manifest = Manifest::new();
manifest.set_section_attribute(
"com/example/".to_string(),
"Implementation-Title".to_string(),
"MyApp".to_string(),
);
assert!(manifest.get_main_attribute("Implementation-Title").is_none());
}
#[test]
fn test_manifest_default_jar_manifest() {
let manifest = Manifest::default_jar_manifest("com.example", "myapp", "1.0.0");
assert_eq!(
manifest.get_main_attribute("Manifest-Version"),
Some(&"1.0".to_string())
);
assert!(manifest.get_main_attribute("Created-By").is_some());
}
#[test]
fn test_manifest_to_bytes() {
let mut manifest = Manifest::new();
manifest.set_main_attribute("Manifest-Version".to_string(), "1.0".to_string());
let bytes = manifest.to_bytes().unwrap();
let content = String::from_utf8(bytes).unwrap();
assert!(content.contains("Manifest-Version: 1.0"));
}
#[test]
fn test_manifest_write_format() {
let mut manifest = Manifest::new();
manifest.set_main_attribute("Manifest-Version".to_string(), "1.0".to_string());
manifest.set_main_attribute("Main-Class".to_string(), "com.example.Main".to_string());
let bytes = manifest.to_bytes().unwrap();
let content = String::from_utf8(bytes).unwrap();
assert!(content.contains("Manifest-Version: 1.0"));
assert!(content.contains("Main-Class: com.example.Main"));
assert!(content.ends_with('\n'));
}
#[test]
fn test_manifest_sections() {
let mut manifest = Manifest::new();
manifest.set_main_attribute("Manifest-Version".to_string(), "1.0".to_string());
manifest.set_section_attribute(
"com/example/".to_string(),
"Implementation-Title".to_string(),
"MyApp".to_string(),
);
let bytes = manifest.to_bytes().unwrap();
let content = String::from_utf8(bytes).unwrap();
assert!(content.contains("Name: com/example/"));
assert!(content.contains("Implementation-Title: MyApp"));
}
}