Skip to content
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
7 changes: 3 additions & 4 deletions core/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
local_src::{LocalSrcError, LocalSrcProject},
utils::{FsIoError, ZipArchiveError, wrapfs},
},
utils::{format_err, license_file_stems, sha256_lowercase_hex},
utils::{format_err, license_file_stems, sha256_lowercase_hex, to_pretty_json_string},
workspace::{Workspace, WorkspaceReadError},
};

Expand Down Expand Up @@ -450,9 +450,8 @@ fn do_build_kpar_inner<P: AsRef<Utf8Path>, Pr: ProjectRead>(
// top level, exactly one file named .project.json and exactly one file
// named .meta.json.”

let info_content =
serde_json::to_string(&info).expect("BUG: failed to serialize .project.json");
let meta_content = serde_json::to_string(&meta).expect("BUG: failed to serialize .meta.json");
let info_content = to_pretty_json_string(&info);
let meta_content = to_pretty_json_string(&meta);

zip.start_file(".project.json", options)
.map_err(|e| ZipArchiveError::Write(Utf8Path::new(".project.json").into(), e))?;
Expand Down
11 changes: 6 additions & 5 deletions core/src/commands/index/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use thiserror::Error;

use super::{
INDEX_FILE_NAME, INFO_FILE_NAME, JsonFileError, KPAR_FILE_NAME, META_FILE_NAME,
VERSIONS_FILE_NAME, open_json_file, overwrite_file, to_json_string,
VERSIONS_FILE_NAME, open_json_file, overwrite_file,
};

use crate::{
Expand All @@ -26,6 +26,7 @@ use crate::{
utils::{FsIoError, wrapfs},
},
purl::{is_valid_unnormalized_name, is_valid_unnormalized_publisher, normalize_field},
utils::to_pretty_json_string,
};

#[derive(Error, Debug)]
Expand Down Expand Up @@ -265,8 +266,8 @@ pub fn do_index_add<I: AsRef<str>, P: AsRef<Utf8Path>, R: AsRef<Utf8Path>>(
[_, _, ..] => return Err(IndexAddError::DuplicateProject { iri: iri.into() }),
};

let info_str = to_json_string(&info);
let meta_str = to_json_string(&meta);
let info_str = to_pretty_json_string(&info);
let meta_str = to_pretty_json_string(&meta);

wrapfs::create_dir_all(&project_path)?;

Expand Down Expand Up @@ -337,8 +338,8 @@ pub fn do_index_add<I: AsRef<str>, P: AsRef<Utf8Path>, R: AsRef<Utf8Path>>(
},
);

let versions_str = to_json_string(&versions_value);
let index_str = to_json_string(&index_value);
let versions_str = to_pretty_json_string(&versions_value);
let index_str = to_pretty_json_string(&index_value);

let adding = "Adding";
let header = crate::style::get_style_config().header;
Expand Down
5 changes: 3 additions & 2 deletions core/src/commands/index/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ use std::{
use camino::Utf8Path;
use thiserror::Error;

use super::{INDEX_FILE_NAME, to_json_string};
use super::INDEX_FILE_NAME;

use crate::{
index::model::IndexJson,
project::utils::{FsIoError, wrapfs},
utils::to_pretty_json_string,
};

#[derive(Error, Debug)]
Expand All @@ -29,7 +30,7 @@ pub fn do_index_init<R: AsRef<Utf8Path>>(index_root: R) -> Result<(), IndexInitE
let header = crate::style::get_style_config().header;
log::info!("{header}{creating:>12}{header:#} index");
let index = IndexJson { projects: vec![] };
let index_str = to_json_string(&index);
let index_str = to_pretty_json_string(&index);
wrapfs::create_dir_all(index_root.as_ref())?;
let index_path = index_root.as_ref().join(INDEX_FILE_NAME);
let mut file = fs::File::create_new(&index_path).map_err(|e| match e.kind() {
Expand Down
5 changes: 0 additions & 5 deletions core/src/commands/index/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,6 @@ pub(crate) fn open_json_file<T: Default + Serialize + DeserializeOwned>(
Ok((file, value))
}

pub(crate) fn to_json_string<T: Serialize>(value: &T) -> String {
// If this fails, it's a bug
serde_json::to_string_pretty(value).unwrap()
}

pub(crate) fn overwrite_file(
file: &mut File,
path: &Utf8Path,
Expand Down
10 changes: 4 additions & 6 deletions core/src/commands/index/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ use std::fs::File;
use camino::{Utf8Path, Utf8PathBuf};
use thiserror::Error;

use super::{
INDEX_FILE_NAME, JsonFileError, VERSIONS_FILE_NAME, open_json_file, overwrite_file,
to_json_string,
};
use super::{INDEX_FILE_NAME, JsonFileError, VERSIONS_FILE_NAME, open_json_file, overwrite_file};

use crate::{
index::{
iri::{ParseIriError, parse_iri},
model::{IndexJson, ProjectStatus, VersionEntry, VersionStatus, VersionsJson},
},
project::utils::{FsIoError, wrapfs},
utils::to_pretty_json_string,
};

#[derive(Debug, Error)]
Expand Down Expand Up @@ -83,7 +81,7 @@ pub fn do_index_remove<I: AsRef<str>, R: AsRef<Utf8Path>>(
} else {
project_entry.status = ProjectStatus::Removed;
}
let index_str = to_json_string(&index_value);
let index_str = to_pretty_json_string(&index_value);
let project_path = index_root.join(parsed_iri.get_path());

let versions_path = project_path.join(VERSIONS_FILE_NAME);
Expand Down Expand Up @@ -168,7 +166,7 @@ fn remove_versions<F: FnMut(&VersionEntry) -> bool>(
let version_path = project_path.join(&version_entry.version);
version_entry.status = VersionStatus::Removed;

let versions_str = to_json_string(&versions_value);
let versions_str = to_pretty_json_string(&versions_value);
overwrite_file(versions_file, versions_path, &versions_str)?;
wrapfs::remove_dir_all(version_path)?;
}
Expand Down
8 changes: 3 additions & 5 deletions core/src/commands/index/yank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
use camino::{Utf8Path, Utf8PathBuf};
use thiserror::Error;

use super::{
INDEX_FILE_NAME, JsonFileError, VERSIONS_FILE_NAME, open_json_file, overwrite_file,
to_json_string,
};
use super::{INDEX_FILE_NAME, JsonFileError, VERSIONS_FILE_NAME, open_json_file, overwrite_file};

use crate::{
index::{
iri::{ParseIriError, parse_iri},
model::{IndexJson, VersionStatus, VersionsJson},
},
project::utils::{FsIoError, wrapfs},
utils::to_pretty_json_string,
};

#[derive(Debug, Error)]
Expand Down Expand Up @@ -100,7 +98,7 @@ pub fn do_index_yank<R: AsRef<Utf8Path>, I: AsRef<str>, V: AsRef<str>>(
match version_entry.status {
VersionStatus::Available => {
version_entry.status = VersionStatus::Yanked;
let versions_str = to_json_string(&versions_value);
let versions_str = to_pretty_json_string(&versions_value);
overwrite_file(&mut versions_file, &versions_path, &versions_str)?;
}
VersionStatus::Yanked => {
Expand Down
9 changes: 9 additions & 0 deletions core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{error::Error, fmt::Write as _};

use digest::{array::Array, typenum};
use indexmap::IndexSet;
use serde::Serialize;
use sha2::{Digest, Sha256};
use thiserror::Error;
use typed_path::{Utf8UnixPath, Utf8WindowsPath};
Expand Down Expand Up @@ -231,3 +232,11 @@ pub fn parse_relative_unix_path(

Ok(Utf8UnixPath::new(path))
}

pub fn to_pretty_json_string<T: Serialize>(value: &T) -> String {
// If this fails, it's a bug
let mut serialized = serde_json::to_string_pretty(value).unwrap();
// Text files should have a trailing newline
serialized.push('\n');
serialized
}
46 changes: 45 additions & 1 deletion sysand/tests/cli_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,45 @@ fn project_build() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

/// `.project.json` and `.meta.json` are pretty-printed (multi-line, indented)
/// inside the built kpar, not minified onto a single line.
#[test]
fn project_build_pretty_prints_project_and_meta_json() -> Result<(), Box<dyn std::error::Error>> {
let (_temp_dir, cwd, out) = run_sysand(
["init", "--version", "1.2.3", "--name", "test_pretty"],
None,
)?;

std::fs::write(cwd.join("test.sysml"), b"package P;\n")?;

out.assert().success();

let out = run_sysand_in(&cwd, ["include", "--no-index-symbols", "test.sysml"], None)?;
out.assert().success();

let out = run_sysand_in(&cwd, ["build", "./test_build.kpar"], None)?;
out.assert().success();

for archive_path in [".project.json", ".meta.json"] {
let content = read_kpar_file(&cwd.join("test_build.kpar"), archive_path);

assert!(
content.lines().count() > 1,
"{archive_path} should be pretty-printed across multiple lines, got: {content}"
);

let value: serde_json::Value = serde_json::from_str(&content)?;
let mut expected = serde_json::to_string_pretty(&value)?;
expected.push('\n');
assert_eq!(
content, expected,
"{archive_path} should use standard pretty-printed formatting"
);
}

Ok(())
}

#[test]
fn build_errors_when_index_symbol_is_missing_from_file() -> Result<(), Box<dyn std::error::Error>> {
let (_temp_dir, cwd, out) = run_sysand(
Expand Down Expand Up @@ -1190,14 +1229,19 @@ fn project_build_without_license() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}

fn assert_kpar_file(kpar_path: &camino::Utf8Path, archive_path: &str, expected: &str) {
fn read_kpar_file(kpar_path: &camino::Utf8Path, archive_path: &str) -> String {
let file = std::fs::File::open(kpar_path).unwrap();
let mut archive = zip::ZipArchive::new(file).unwrap();
let mut entry = archive
.by_name(archive_path)
.unwrap_or_else(|_| panic!("expected {archive_path} in {kpar_path}"));
let mut content = String::new();
entry.read_to_string(&mut content).unwrap();
content
}

fn assert_kpar_file(kpar_path: &camino::Utf8Path, archive_path: &str, expected: &str) {
let content = read_kpar_file(kpar_path, archive_path);
assert_eq!(content, expected, "{archive_path} mismatch in {kpar_path}");
}

Expand Down
Loading