8000 feat(si-pkg,object-tree): add optional kind key to SiPkg by zacharyhamm · Pull Request #2709 · systeminit/si · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(si-pkg,object-tree): add optional kind key to SiPkg #2709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 69 additions & 8 deletions lib/object-tree/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub const NL: char = '\n';
#[remain::sorted]
#[derive(Debug, Error)]
pub enum GraphError {
/// When an attempt to get a slice from the BufRead internal buffer fails
#[error("could not get an expected range from the BufRead internal buffer")]
BufReadRangeError,
/// When a checked arithmetic operation returns [`None`]
#[error("checked arithmetic failed: {0}")]
CheckedArithmeticFailure(&'static str),
Expand Down Expand Up @@ -712,6 +715,71 @@ where
}
}

fn parse_key_line(line: &str) -> Result<(&str, usize, &str), GraphError> {
let (line_key_and_len, line_value) = match line.split_once('=') {
Some((key_and_len, value)) => (key_and_len, value),
None => return Err(GraphError::ParseLineKeyValueFormat(line.into())),
};

let (line_key, len) = match line_key_and_len.split_once(':') {
Some((key, len_str)) => (key, usize::from_str(len_str).map_err(GraphError::parse)?),
None => return Err(GraphError::ParseLineKeyValueFormat(line.into())),
};

Ok((line_key, len, line_value))
}

/// Attempts to read a key/value formatted line for the given key, returning the value as a
/// `Option<String>`.
///
/// If the key does not match (or if the file is at EOF, or if the key is larger than the BufRead
/// internal buffer of 8Kb), returns None, but does not consume the buffer, allowing us to try and
/// read a different key value line.
///
/// Used for reading key value pairs that may or may not be present in an object.
///
/// # Errors
///
/// Returns an `Err` if
///
/// - An I/O error occurs while reading from the reader
/// - If the line does not parse as a key/value line
/// - If the key name in the parsed line does not match the expected key name
pub fn read_key_value_line_opt<R: BufRead>(
reader: &mut R,
key: impl AsRef<str>,
) -> Result<Option<String>, GraphError> {
const ASCII_COLON: u8 = 58;

let key_bytes = key.as_ref().as_bytes();
let current_buf = reader.fill_buf().map_err(GraphError::IoRead)?;
if current_buf.len() < key_bytes.len().saturating_add(1) {
return Ok(None);
}

let current_buf_key = current_buf
.get(..key_bytes.len())
.ok_or(GraphError::BufReadRangeError)?;

match std::str::from_utf8(current_buf_key) {
// If this fails its because we cut the utf8 off short in the middle of a multibyte run.
// If that's the case, our key does not match.
Err(_) => Ok(None),
Ok(utf8_buf_key) => {
if utf8_buf_key == key.as_ref()
&& current_buf
.get(key_bytes.len())
.ok_or(GraphError::BufReadRangeError)?
== &ASCII_COLON
{
Ok(Some(read_key_value_line(reader, key)?))
} else {
Ok(None)
}
}
}
}

/// Reads a key/value formatted line from a reader and returns the value as a `String`.
///
/// # Errors
Expand All @@ -727,15 +795,8 @@ pub fn read_key_value_line<R: BufRead>(
) -> Result<String, GraphError> {
let mut line = String::new();
reader.read_line(&mut line).map_err(GraphError::IoRead)?;
let (line_key_and_len, line_value) = match line.split_once('=') {
Some((key_and_len, value)) => (key_and_len, value),
None => return Err(GraphError::ParseLineKeyValueFormat(line)),
};

let (line_key, len) = match line_key_and_len.split_once(':') {
Some((key, len_str)) => (key, usize::from_str(len_str).map_err(GraphError::parse)?),
None => return Err(GraphError::ParseLineKeyValueFormat(line)),
};
let (line_key, len, line_value) = parse_key_line(&line)?;

if line_key != key.as_ref() {
return Err(GraphError::ParseLineExpectedKey(
Expand Down
4 changes: 2 additions & 2 deletions lib/object-tree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub use crate::tar::{
write::{TarWriter, TarWriterError},
};
pub use graph::{
read_key_value_line, write_key_value_line, GraphError, HashedNode, NameStr, NodeChild,
NodeKind, NodeWithChildren, ObjectTree, ReadBytes, WriteBytes,
read_key_value_line, read_key_value_line_opt, write_key_value_line, GraphError, HashedNode,
NameStr, NodeChild, NodeKind, NodeWithChildren, ObjectTree, ReadBytes, WriteBytes,
};
pub use hash::{Hash, HashParseError};
2 changes: 1 addition & 1 deletion lib/si-pkg/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ rust_library(
"//third-party/rust:url",
],
srcs = glob(["src/**/*.rs"]),
test_unit_srcs = ["pkg-complex.json", "pkg-simple.json"],
test_unit_srcs = ["pkg-complex.json", "pkg-workspace.json"],
test_unit_deps = [
"//third-party/rust:tempfile",
],
Expand Down
1 change: 1 addition & 0 deletions lib/si-pkg/pkg-complex.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"kind": "module",
"name": "complex",
"version": "12.11.0 ",
"description": "complex\nthings\nwith\nmultiple\nlines\n\n\n",
Expand Down
55 changes: 0 additions & 55 deletions lib/si-pkg/pkg-simple.json

This file was deleted.

10 changes: 10 additions & 0 deletions lib/si-pkg/pkg-workspace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"kind": "workspaceBackup",
"name": "Workspace Backup",
"version": "12.11.0 ",
"description": "complex\nthings\nwith\nmultiple\nlines\n\n\n",
"createdAt": "2023-08-28T00:19:25Z",
"createdBy": "zacharyhamm",
"funcs": [],
"schemas": []
}
35 changes: 34 additions & 1 deletion lib/si-pkg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod spec;

pub use pkg::{
SiPkg, SiPkgActionFunc, SiPkgAttrFuncInput, SiPkgAttrFuncInputView, SiPkgError, SiPkgFunc,
SiPkgFuncDescription, SiPkgLeafFunction, SiPkgMapKeyFunc, SiPkgMetadata, SiPkgProp,
SiPkgFuncDescription, SiPkgKind, SiPkgLeafFunction, SiPkgMapKeyFunc, SiPkgMetadata, SiPkgProp,
SiPkgSchema, SiPkgSchemaVariant, SiPkgSocket, SiPkgValidation,
};
pub use spec::{
Expand All @@ -29,6 +29,7 @@ mod tests {
use super::*;

const PACKAGE_JSON: &str = include_str!("../pkg-complex.json");
const WORKSPACE_JSON: &str = include_str!("../pkg-workspace.json");

pub async fn prop_visitor(
prop: SiPkgProp<'_>,
Expand Down Expand Up @@ -57,6 +58,33 @@ mod tests {
println!("\n---- snip ----\n{:?}\n---- snip ----", Dot::new(graph));
}

#[tokio::test]
async fn pkg_workspace_round_trip() {
let spec: PkgSpec = serde_json::from_str(WORKSPACE_JSON).unwrap();
let description = spec.description.to_owned();
let pkg = SiPkg::load_from_spec(spec).expect("failed to load spec");

let pkg_data = pkg.write_to_bytes().expect("failed to serialize pkg");

let read_pkg = SiPkg::load_from_bytes(pkg_data).expect("failed to load pkg from bytes");

assert_eq!(
description,
read_pkg
.metadata()
.expect("get metadata (WorkspaceBackup)")
.description()
);

assert_eq!(
SiPkgKind::WorkspaceBackup,
read_pkg
.metadata()
.expect("get metadata for kind (WorkspaceBackup)")
.kind()
);
}

#[tokio::test]
async fn pkg_bytes_round_trip() {
let spec: PkgSpec = serde_json::from_str(PACKAGE_JSON).unwrap();
Expand All @@ -72,6 +100,11 @@ mod tests {
read_pkg.metadata().expect("get metadata").description()
);

assert_eq!(
SiPkgKind::Module,
read_pkg.metadata().expect("get metadata for kind").kind()
);

let funcs = read_pkg.funcs().expect("failed to get funcs");
assert_eq!(2, funcs.len());

Expand Down
20 changes: 16 additions & 4 deletions lib/si-pkg/src/node/package.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
use std::io::{BufRead, Write};
use std::{
io::{BufRead, Write},
str::FromStr,
};

use chrono::{DateTime, Utc};
use object_tree::{
read_key_value_line, write_key_value_line, GraphError, NameStr, NodeChild, NodeKind,
NodeWithChildren, ReadBytes, WriteBytes,
read_key_value_line, read_key_value_line_opt, write_key_value_line, GraphError, NameStr,
NodeChild, NodeKind, NodeWithChildren, ReadBytes, WriteBytes,
};

use crate::PkgSpec;
use crate::{PkgSpec, SiPkgKind};

use super::{category::PackageCategory, PkgNode};

const KEY_CREATED_AT_STR: &str = "created_at";
const KEY_CREATED_BY_STR: &str = "created_by";
const KEY_DESCRIPTION_STR: &str = "description";
const KEY_KIND_STR: &str = "kind";
const KEY_NAME_STR: &str = "name";
const KEY_VERSION_STR: &str = "version";

#[derive(Clone, Debug)]
pub struct PackageNode {
pub kind: SiPkgKind,
pub name: String,
pub version: String,

Expand All @@ -34,6 +39,7 @@ impl NameStr for PackageNode {

impl WriteBytes for PackageNode {
fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<(), GraphError> {
write_key_value_line(writer, KEY_KIND_STR, &self.kind.to_string())?;
write_key_value_line(writer, KEY_NAME_STR, self.name())?;
write_key_value_line(writer, KEY_VERSION_STR, &self.version)?;
write_key_value_line(writer, KEY_DESCRIPTION_STR, &self.description)?;
Expand All @@ -48,6 +54,10 @@ impl ReadBytes for PackageNode {
where
Self: std::marker::Sized,
{
let kind = match read_key_value_line_opt(reader, KEY_KIND_STR)? {
None => SiPkgKind::Module,
Some(kind_str) => SiPkgKind::from_str(&kind_str).map_err(GraphError::parse)?,
};
let name = read_key_value_line(reader, KEY_NAME_STR)?;
let version = read_key_value_line(reader, KEY_VERSION_STR)?;
let description = read_key_value_line(reader, KEY_DESCRIPTION_STR)?;
Expand All @@ -58,6 +68,7 @@ impl ReadBytes for PackageNode {
let created_by = read_key_value_line(reader, KEY_CREATED_BY_STR)?;

Ok(Self {
kind,
name,
version,
description,
Expand All @@ -74,6 +85,7 @@ impl NodeChild for PkgSpec {
NodeWithChildren::new(
NodeKind::Tree,
Self::NodeType::Package(PackageNode {
kind: self.kind,
name: self.name.to_string(),
version: self.version.to_string(),
description: self.description.to_string(),
Expand Down
30 changes: 30 additions & 0 deletions lib/si-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use object_tree::{
TarWriterError,
};
use petgraph::prelude::*;
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, EnumIter, EnumString};
use thiserror::Error;

mod action_func;
Expand Down Expand Up @@ -196,6 +198,7 @@ impl SiPkg {
let metadata = self.metadata()?;

builder
.kind(metadata.kind())
.name(metadata.name())
.description(metadata.description())
.version(metadata.version())
Expand Down Expand Up @@ -290,8 +293,30 @@ impl<'a> fmt::Debug for Source<'a> {
}
}

#[remain::sorted]
#[derive(
Debug,
Serialize,
Deserialize,
Clone,
PartialEq,
Eq,
AsRefStr,
Display,
EnumIter,
EnumString,
Copy,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum SiPkgKind {
Module,
WorkspaceBackup,
}

#[derive(Clone, Debug)]
pub struct SiPkgMetadata {
kind: SiPkgKind,
name: String,
version: String,
description: String,
Expand All @@ -315,6 +340,7 @@ impl SiPkgMetadata {
};

Ok(Self {
kind: metadata_node.kind,
name: metadata_node.name,
version: metadata_node.version,
description: metadata_node.description,
Expand All @@ -324,6 +350,10 @@ impl SiPkgMetadata {
})
}

pub fn kind(&self) -> SiPkgKind {
self.kind
}

pub fn name(&self) -> &str {
self.name.as_ref()
}
Expand Down
Loading
0