use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]
#[serde(default)]
pub struct SnapcraftManifest {
pub adopt_info: String,
pub architectures: Vec<String>,
pub assumes: Vec<String>,
pub base: String,
pub confinement: String,
pub description: String,
pub grade: String,
pub icon: String,
pub license: String,
pub name: String,
pub summary: String,
pub title: String,
pub r#type: String,
pub version: String,
pub default_version: String,
pub plugs: HashMap<String, SnapcraftPlug>,
pub apps: HashMap<String, SnapcraftApp>,
pub parts: HashMap<String, SnapcraftPart>,
}
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]
#[serde(default)]
pub struct SnapcraftPlug {
pub interface: String,
pub target: String,
pub default_provider: String,
}
const REQUIRED_TOP_LEVEL_FIELDS: [&'static str; 0] = [
];
enum Confinement {
Strict,
Devmode,
Classic,
}
enum Grade {
Stable,
Devel,
}
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]
#[serde(default)]
pub struct SnapcraftApp {
pub adapter: String,
pub autostart: String,
pub command: String,
pub command_chain: Vec<String>,
pub common_id: String,
pub daemon: String,
pub desktop: String,
pub environment: HashMap<String, String>,
pub extensions: Vec<String>,
pub plugs: Vec<String>,
pub post_stop_command: String,
pub restart_condition: String,
pub slots: Vec<String>,
pub socket: HashMap<String, SnapcraftSocket>,
pub stop_command: String,
pub stop_timeout: String,
pub timer: String,
}
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]
#[serde(default)]
pub struct SnapcraftSocket {
pub listen_stream: String,
pub socket_mode: String,
}
pub const ALLOWED_RESTARTS_CONDITIONS: [&str; 6] = ["on_failure", "on_success", "on_abnormal", "on_abort", "always", "never"];
pub const ALLOWED_DAEMON_TYPES: [&str; 4] = [
"simple", "oneshot", "forking", "notify",
];
pub const ALLOWED_BUILD_ATTRIBUTES: [&str; 4] = [
"debug",
"keep_execstack",
"no_patchelf",
"no_install",
];
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]
#[serde(default)]
pub struct SnapcraftPart {
pub after: Vec<String>,
pub build_attributes: Vec<String>,
pub build_environment: HashMap<String, String>,
pub build_packages: Vec<String>,
pub build_snaps: Vec<String>,
pub filesets: Vec<String>,
pub install: String,
pub organize: HashMap<String, String>,
pub override_build: String,
pub override_prime: String,
pub override_pull: String,
pub override_stage: String,
pub parse_info: String,
pub plugin: String,
pub prepare: String,
pub prime: Vec<String>,
pub source: String,
pub source_branch: String,
pub source_checksum: String,
pub source_commit: String,
pub source_depth: i32,
pub source_subdir: String,
pub source_tag: String,
pub source_type: String,
pub stage: Vec<String>,
pub stage_packages: Vec<SnapcraftPackage>,
pub stage_snaps: Vec<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[serde(untagged)]
pub enum SnapcraftPackage {
PackageName(String),
OptionalPackages(SnapcraftOptionalPackages),
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct SnapcraftOptionalPackages {
r#try: Vec<String>,
}
pub fn parse(ctx: &mut crate::execution_context::ExecutionContext) {
ctx.manifest = crate::manifests::manifest::AbstractManifest::default();
let snapcraft_manifest: SnapcraftManifest = match serde_yaml::from_str(&ctx.content) {
Ok(m) => m,
Err(e) => panic!("Failed to parse the Snapcraft manifest: {}.", e),
};
if snapcraft_manifest.name.is_empty() {
panic!("Required top-level field name is missing from snapcraft manifest.");
}
if snapcraft_manifest.grade.is_empty() {
panic!("Required top-level field grade is missing from snapcraft manifest.");
}
ctx.manifest.snap_manifest = Some(snapcraft_manifest);
}
pub fn file_path_matches(path: &str) -> bool {
if path.to_lowercase().ends_with("snapcraft.yaml") {
return true;
}
if path.to_lowercase().ends_with("snapcraft.yml") {
return true;
}
return false;
}
pub fn file_content_matches(content: &str) -> bool {
return false;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn test_file_path_matches() {
assert!(file_path_matches("snapcraft.yaml"));
assert!(file_path_matches("/path/to/snapcraft.yml"));
assert!(file_path_matches("/path/to/Snapcraft.YAML"));
assert!(!file_path_matches("/path/to/file.yaml"));
assert!(!file_path_matches("/path/to/file.json"));
assert!(!file_path_matches(""));
assert!(!file_path_matches("/////////////"));
}
#[test]
#[should_panic(expected = "Required top-level field grade is missing from snapcraft manifest.")]
pub fn test_parse_missing_required_fields() {
let mut ctx = crate::execution_context::ExecutionContext::default();
ctx.content = r###"
name: app-name,
description: description
summary: this is my app,
version: 0.0.1
"###
.to_string();
parse(&mut ctx);
}
#[test]
#[should_panic(expected = "Failed to parse the Snapcraft manifest: EOF while parsing a value.")]
pub fn test_parse_empty_string() {
let mut ctx = crate::execution_context::ExecutionContext::default();
ctx.content = "".to_string();
parse(&mut ctx);
}
#[test]
#[should_panic]
pub fn test_parse_invalid_yaml() {
let mut ctx = crate::execution_context::ExecutionContext::default();
ctx.content = "----------------------------".to_string();
parse(&mut ctx);
}
#[test]
pub fn test_parse_missing_version() {
let mut ctx = crate::execution_context::ExecutionContext::default();
ctx.content = r###"
name: app-name
description: description
grade: devel
summary: this is my app
"###
.to_string();
parse(&mut ctx);
match ctx.manifest.snap_manifest {
None => panic!("Error while parsing the snap manifest."),
Some(manifest) => {
assert_eq!(manifest.name, "app-name");
}
}
}
}