Skip to content

Commit

Permalink
Start implementing accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
theory committed Sep 10, 2024
1 parent 9a45fb3 commit a931128
Show file tree
Hide file tree
Showing 2 changed files with 306 additions and 4 deletions.
120 changes: 116 additions & 4 deletions src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Spec `META.json` files. It supports both the [v1] and [v2] specs.
[v2]: https://github.com/pgxn/rfcs/pull/3
*/
use std::{collections::HashMap, error::Error, fs::File, path::PathBuf};
use std::{borrow::Borrow, collections::HashMap, error::Error, fs::File, path::PathBuf};

use crate::util;
use relative_path::RelativePathBuf;
Expand All @@ -22,11 +22,23 @@ mod v2;
/// Represents the `meta-spec` object in [`Meta`].
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Spec {
version: String,
version: Version,
#[serde(skip_serializing_if = "Option::is_none")]
url: Option<String>,
}

impl Spec {
/// Borrows the Spec version.
pub fn version(&self) -> &Version {
self.version.borrow()
}

/// Borrows the Spec URL.
pub fn url(&self) -> Option<&String> {
self.url.as_ref()
}
}

/// Maintainer represents an object in the list of `maintainers` in [`Meta`].
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Maintainer {
Expand All @@ -37,6 +49,23 @@ pub struct Maintainer {
url: Option<String>,
}

impl Maintainer {
/// Borrows the Maintainer name.
pub fn name(&self) -> &str {
self.name.as_str()
}

/// Borrows the Maintainer email.
pub fn email(&self) -> Option<&String> {
self.email.as_ref()
}

/// Borrows the Maintainer URL.
pub fn url(&self) -> Option<&String> {
self.url.as_ref()
}
}

/// Describes an extension in under `extensions` in [`Contents`].
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Extension {
Expand All @@ -51,26 +80,82 @@ pub struct Extension {
doc: Option<RelativePathBuf>,
}

impl Extension {
/// Borrows the Extension control file location.
pub fn control(&self) -> &RelativePathBuf {
self.control.borrow()
}

/// Borrows the Extension abstract.
pub fn abs_tract(&self) -> Option<&String> {
self.abs_tract.as_ref()
}

/// Returns true if the Extension is marked as a trusted language
/// extension.
pub fn tle(&self) -> bool {
self.tle.unwrap_or(false)
}

/// Borrows the Extension sql file location.
pub fn sql(&self) -> &RelativePathBuf {
self.sql.borrow()
}

/// Borrows the Extension doc file location.
pub fn doc(&self) -> Option<&RelativePathBuf> {
self.doc.as_ref()
}
}

/// Defines a type of module in [`Module`].
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum ModuleType {
pub enum ModuleType {
/// Indicates an extension shared library module.
#[serde(rename = "extension")]
Extension,
/// Indicates a hook shared library module.
#[serde(rename = "hook")]
Hook,
/// Indicates a background worker shared library module.
#[serde(rename = "bgw")]
Bgw,
}

impl std::fmt::Display for ModuleType {
/// fmt writes the sting representation of the ModuleType to f.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ModuleType::Extension => write!(f, "extension"),
ModuleType::Hook => write!(f, "hook"),
ModuleType::Bgw => write!(f, "bgw"),
}
}
}

/// Defines the values for the `preload` value in [`Module`]s.
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum Preload {
pub enum Preload {
/// Indicates a module that should be included in
/// `shared_preload_libraries` and requires a service restart.
#[serde(rename = "server")]
Server,
/// Indicates a module that can be loaded in a session via
/// `session_preload_libraries` or `local_preload_libraries`.
#[serde(rename = "session")]
Session,
}

impl std::fmt::Display for Preload {
/// fmt writes the sting representation of the ModuleType to f.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Preload::Server => write!(f, "server"),
Preload::Session => write!(f, "session"),
}
}
}

/// Represents a loadable module under `modules` in [`Contents`].
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Module {
Expand All @@ -86,6 +171,33 @@ pub struct Module {
doc: Option<RelativePathBuf>,
}

impl Module {
/// Borrows the Module type.
pub fn kind(&self) -> &ModuleType {
self.kind.borrow()
}

/// Borrows the Module abstract.
pub fn abs_tract(&self) -> Option<&String> {
self.abs_tract.as_ref()
}

/// Borrows the Module preload value.
pub fn preload(&self) -> Option<&Preload> {
self.preload.as_ref()
}

/// Borrows the Module library file location.
pub fn lib(&self) -> &RelativePathBuf {
self.lib.borrow()
}

/// Borrows the Module doc file location.
pub fn doc(&self) -> Option<&RelativePathBuf> {
self.doc.as_ref()
}
}

/// Represents an app under `apps` in [`Contents`].
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct App {
Expand Down
190 changes: 190 additions & 0 deletions src/meta/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,193 @@ fn test_try_merge_partman() -> Result<(), Box<dyn Error>> {

Ok(())
}

#[test]
fn test_spec() {
for (name, json) in [
(
"both",
json!({"version": "2.0.0", "url": "https://example.com"}),
),
("version only", json!({"version": "2.0.4"})),
] {
let spec: Spec = serde_json::from_value(json.clone()).unwrap();
assert_eq!(
json.get("version").unwrap().as_str().unwrap(),
spec.version().to_string(),
"{name}",
);
match json.get("url") {
None => assert!(spec.url().is_none()),
Some(url) => assert_eq!(url.as_str().unwrap(), spec.url().unwrap()),
}
}
}

#[test]
fn test_maintainer() {
for (name, json) in [
(
"all fields",
json!({
"name": "Barrack Obama",
"email": "[email protected]",
"url": "https://potus.example.com",
}),
),
(
"name and email",
json!({
"name": "Barrack Obama",
"email": "[email protected]",
}),
),
(
"name and url",
json!({
"name": "Barrack Obama",
"url": "https://potus.example.com",
}),
),
] {
let maintainer: Maintainer = serde_json::from_value(json.clone()).unwrap();
assert_eq!(
json.get("name").unwrap().as_str().unwrap(),
maintainer.name().to_string(),
"{name}",
);
match json.get("email") {
None => assert!(maintainer.email().is_none()),
Some(email) => assert_eq!(email.as_str().unwrap(), maintainer.email().unwrap()),
}
match json.get("url") {
None => assert!(maintainer.url().is_none()),
Some(url) => assert_eq!(url.as_str().unwrap(), maintainer.url().unwrap()),
}
}
}

#[test]
fn test_extension() {
for (name, json) in [
(
"all fields",
json!({
"control": "pair.control",
"abstract": "We have assumed control",
"tle": true,
"sql": "pair.sql",
"doc": "doc/pair.md",
}),
),
(
"minimal",
json!({
"control": "pair.control",
"sql": "pair.sql",
}),
),
(
"false tle",
json!({
"control": "pair.control",
"tle": false,
"sql": "pair.sql",
}),
),
] {
let extension: Extension = serde_json::from_value(json.clone()).unwrap();
assert_eq!(
json.get("control").unwrap().as_str().unwrap(),
extension.control().to_string(),
"{name} control",
);
assert_eq!(
json.get("sql").unwrap().as_str().unwrap(),
extension.sql().to_string(),
"{name} sql",
);
let tle = match json.get("tle") {
None => false,
Some(tle) => tle.as_bool().unwrap(),
};
assert_eq!(tle, extension.tle());
match json.get("abstract") {
None => assert!(extension.abs_tract().is_none()),
Some(abs) => assert_eq!(abs.as_str().unwrap(), extension.abs_tract().unwrap()),
}
match json.get("doc") {
None => assert!(extension.doc().is_none()),
Some(doc) => assert_eq!(doc.as_str().unwrap(), extension.doc().unwrap()),
}
}
}

#[test]
fn test_module_type() {
for (name, mod_type) in [
("extension", ModuleType::Extension),
("hook", ModuleType::Hook),
("bgw", ModuleType::Bgw),
] {
let mt: ModuleType = serde_json::from_value(json!(name)).unwrap();
assert_eq!(mod_type, mt);
assert_eq!(name, mt.to_string())
}
}

#[test]
fn test_preload() {
for (name, preload) in [("server", Preload::Server), ("session", Preload::Session)] {
let pre: Preload = serde_json::from_value(json!(name)).unwrap();
assert_eq!(preload, pre);
assert_eq!(name, pre.to_string())
}
}

#[test]
fn test_module() {
for (name, json) in [
(
"all fields",
json!({
"type": "hook",
"lib": "lib/my_hook",
"doc": "doc/my_hook.md",
"preload": "session",
"abstract": "My hook"
}),
),
(
"minimal",
json!({
"type": "extension",
"lib": "lib/my_hook",
}),
),
] {
let module: Module = serde_json::from_value(json.clone()).unwrap();
assert_eq!(
json.get("type").unwrap().as_str().unwrap(),
module.kind().to_string(),
"{name} type",
);
assert_eq!(
json.get("lib").unwrap().as_str().unwrap(),
module.lib().to_string(),
"{name} lib",
);
match json.get("preload") {
None => assert!(module.preload().is_none()),
Some(pre) => assert_eq!(pre.as_str().unwrap(), module.preload().unwrap().to_string()),
}
match json.get("abstract") {
None => assert!(module.abs_tract().is_none()),
Some(abs) => assert_eq!(abs.as_str().unwrap(), module.abs_tract().unwrap()),
}
match json.get("doc") {
None => assert!(module.doc().is_none()),
Some(doc) => assert_eq!(doc.as_str().unwrap(), module.doc().unwrap()),
}
}
}

0 comments on commit a931128

Please sign in to comment.