aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/json
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/json')
-rw-r--r--crates/shirabe/src/json/json_file.rs70
-rw-r--r--crates/shirabe/src/json/json_manipulator.rs92
2 files changed, 80 insertions, 82 deletions
diff --git a/crates/shirabe/src/json/json_file.rs b/crates/shirabe/src/json/json_file.rs
index d08e207..52c3c71 100644
--- a/crates/shirabe/src/json/json_file.rs
+++ b/crates/shirabe/src/json/json_file.rs
@@ -3,7 +3,8 @@
use crate::io::io_interface;
use crate::util::silencer::Silencer;
use anyhow::Result;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::json_schema::validator::Validator;
use shirabe_external_packages::seld::json_lint::json_parser::JsonParser;
use shirabe_external_packages::seld::json_lint::parsing_exception::ParsingException;
@@ -28,7 +29,7 @@ pub struct JsonFile {
/// @var string
path: String,
/// @var ?HttpDownloader
- http_downloader: Option<HttpDownloader>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
/// @var ?IOInterface
io: Option<Box<dyn IOInterface>>,
/// @var string
@@ -67,10 +68,10 @@ impl JsonFile {
/// @throws \InvalidArgumentException
pub fn new(
path: String,
- http_downloader: Option<HttpDownloader>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
io: Option<Box<dyn IOInterface>>,
) -> Result<Self> {
- if http_downloader.is_none() && Preg::is_match(r"{^https?://}i", &path) {
+ if http_downloader.is_none() && Preg::is_match(r"{^https?://}i", &path).unwrap_or(false) {
return Err(InvalidArgumentException {
message: "http urls require a HttpDownloader instance to be passed".to_string(),
code: 0,
@@ -103,7 +104,11 @@ impl JsonFile {
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
let json: Option<String> = match (|| -> Result<Option<String>> {
if let Some(http_downloader) = &self.http_downloader {
- Ok(Some(http_downloader.get(&self.path)?.get_body()))
+ Ok(http_downloader
+ .borrow_mut()
+ .get(&self.path, indexmap::IndexMap::new())?
+ .get_body()
+ .map(|s| s.to_string()))
} else {
if !Filesystem::is_readable(&self.path) {
return Err(RuntimeException {
@@ -120,8 +125,8 @@ impl JsonFile {
realpath_info = format!(" ({})", realpath);
}
}
- io.write_error(
- PhpMixed::String(format!("Reading {}{}", self.path, realpath_info)),
+ io.write_error3(
+ &format!("Reading {}{}", self.path, realpath_info),
true,
io_interface::NORMAL,
);
@@ -160,18 +165,22 @@ impl JsonFile {
Self::parse_json(Some(&json), Some(&self.path))
}
+ pub fn write(&self, hash: PhpMixed) -> Result<()> {
+ self.write2(
+ hash,
+ JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
+ )
+ }
+
/// Writes json file.
///
/// @param mixed[] $hash writes hash into json file
/// @param int $options json_encode options
/// @throws \UnexpectedValueException|\Exception
/// @return void
- pub fn write(&self, hash: PhpMixed, options: i64) -> Result<()> {
+ pub fn write2(&self, hash: PhpMixed, options: i64) -> Result<()> {
if self.path == "php://memory" {
- file_put_contents(
- &self.path,
- Self::encode(&hash, options, &self.indent).as_bytes(),
- );
+ file_put_contents(&self.path, Self::encode(&hash, options).as_bytes());
return Ok(());
}
@@ -207,7 +216,7 @@ impl JsonFile {
&self.path,
&format!(
"{}{}",
- Self::encode(&hash, options, &self.indent),
+ Self::encode(&hash, options),
if options & JSON_PRETTY_PRINT != 0 {
"\n"
} else {
@@ -362,29 +371,13 @@ impl JsonFile {
let mut validator = Validator::new();
// convert assoc arrays to objects
let data_converted = json_decode(&json_encode_ex(data, 0).unwrap_or_default(), false)?;
- validator.validate(&data_converted, &schema_data);
+ validator.check(&data_converted, &schema_data)?;
if !validator.is_valid() {
- let mut errors: Vec<String> = vec![];
- for error in validator.get_errors() {
- let property = error
- .get("property")
- .and_then(|v| v.as_string())
- .unwrap_or("");
- let message = error
- .get("message")
- .and_then(|v| v.as_string())
- .unwrap_or("");
- errors.push(format!(
- "{}{}",
- if !property.is_empty() {
- format!("{} : ", property)
- } else {
- String::new()
- },
- message,
- ));
- }
+ // TODO(phase-b): Validator::get_errors currently returns Vec<String>; original PHP
+ // exposes [{property, message}, ...]. Until shim is enriched, surface raw error
+ // strings without prop/message splitting.
+ let errors: Vec<String> = validator.get_errors();
return Err(JsonValidationException::new(
format!("\"{}\" does not match the expected JSON schema", source),
errors,
@@ -426,7 +419,7 @@ impl JsonFile {
move |m| -> String {
str_repeat(
&indent,
- (strlen(m.get(0).map(|s| s.as_str()).unwrap_or("")) / 4) as usize,
+ (strlen(m.get(&0).map(|s| s.as_str()).unwrap_or("")) / 4) as usize,
)
},
&json,
@@ -545,8 +538,11 @@ impl JsonFile {
}
pub fn detect_indenting(json: Option<&str>) -> String {
- if let Some(m) = Preg::is_match_strict_groups(r##"#^([ \t]+)"#m"##, json.unwrap_or("")) {
- return m.get(1).cloned().unwrap_or_default();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r##"#^([ \t]+)"#m"##, json.unwrap_or(""), Some(&mut m))
+ .unwrap_or(false)
+ {
+ return m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
}
Self::INDENT_DEFAULT.to_string()
diff --git a/crates/shirabe/src/json/json_manipulator.rs b/crates/shirabe/src/json/json_manipulator.rs
index b9c6a5c..c776e13 100644
--- a/crates/shirabe/src/json/json_manipulator.rs
+++ b/crates/shirabe/src/json/json_manipulator.rs
@@ -36,7 +36,7 @@ impl JsonManipulator {
if contents == "" {
contents = "{}".to_string();
}
- if !Preg::is_match("#^\\{(.*)\\}$#s", &contents, None).unwrap_or(false) {
+ if !Preg::is_match3("#^\\{(.*)\\}$#s", &contents, None).unwrap_or(false) {
return Err(InvalidArgumentException {
message: "The json file must be an object ({})".to_string(),
code: 0,
@@ -72,7 +72,7 @@ impl JsonManipulator {
constraint: &str,
sort_packages: bool,
) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no link of that type yet
if decoded.as_array().and_then(|a| a.get(r#type)).is_none() {
@@ -137,7 +137,7 @@ impl JsonManipulator {
);
} else {
let mut groups: Vec<String> = vec![];
- if Preg::is_match_strict_groups(
+ if Preg::is_match_strict_groups3(
"#^\\s*\\{\\s*\\S+.*?(\\s*\\}\\s*)$#s",
&links,
Some(&mut groups),
@@ -178,7 +178,7 @@ impl JsonManipulator {
}
if sort_packages {
- let mut requirements = json_decode(&links, true);
+ let mut requirements = json_decode(&links, true)?;
Self::sort_packages(&mut requirements);
links = self.format(&requirements, 0, false)?;
}
@@ -262,7 +262,7 @@ impl JsonManipulator {
}
fn do_convert_repositories_from_assoc_to_list(&mut self) -> anyhow::Result<bool> {
- let decoded = json_decode(&self.contents, false);
+ let decoded = json_decode(&self.contents, false)?;
let repositories_value = decoded.as_object().and_then(|o| o.get("repositories"));
let is_std_class = repositories_value
@@ -322,7 +322,7 @@ impl JsonManipulator {
}
pub fn set_repository_url(&mut self, name: &str, url: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let mut repository_index: Option<PhpMixed> = None;
let repos = decoded
@@ -378,7 +378,7 @@ impl JsonManipulator {
{
// invalid match due to un-regexable content, abort
let raw_repo = matches.get("repository").cloned().unwrap_or_default();
- if json_decode(&raw_repo, false).as_bool() == Some(false) {
+ if json_decode(&raw_repo, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -398,8 +398,7 @@ impl JsonManipulator {
format!(
"{}{}{}",
repository_matches.get("start").cloned().unwrap_or_default(),
- JsonFile::encode(&PhpMixed::String(url_owned.clone()), 0)
- .unwrap_or_default(),
+ JsonFile::encode(&PhpMixed::String(url_owned.clone()), 0),
repository_matches.get("end").cloned().unwrap_or_default()
)
}
@@ -431,7 +430,7 @@ impl JsonManipulator {
}
let mut index_to_insert: Option<i64> = None;
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let repos = decoded
.as_array()
@@ -499,7 +498,7 @@ impl JsonManipulator {
}
fn do_remove_repository(&mut self, name: &str) -> anyhow::Result<bool> {
- let decoded = json_decode(&self.contents, false);
+ let decoded = json_decode(&self.contents, false)?;
let repositories_value = decoded.as_object().and_then(|o| o.get("repositories"));
let is_assoc = repositories_value
.map(|v| v.as_any().is::<StdClass>())
@@ -623,7 +622,7 @@ impl JsonManipulator {
value: PhpMixed,
append: bool,
) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let mut name_owned = name.to_string();
let mut sub_name: Option<String> = None;
@@ -690,8 +689,8 @@ impl JsonManipulator {
let mut children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, false).is_null()
- || json_decode(&children, false).as_bool() == Some(false)
+ if json_decode(&children, false)?.is_null()
+ || json_decode(&children, false)?.as_bool() == Some(false)
{
return Ok(false);
}
@@ -715,7 +714,8 @@ impl JsonManipulator {
Box::new(move |matches: &IndexMap<String, String>| -> String {
let mut value_local = value_capture.clone();
if sub_name_capture.is_some() && matches.get("content").is_some() {
- let mut cur_val = json_decode(matches.get("content").unwrap(), true);
+ let mut cur_val = json_decode(matches.get("content").unwrap(), true)
+ .unwrap_or(PhpMixed::Null);
if !is_array(&cur_val) {
cur_val = PhpMixed::Array(IndexMap::new());
}
@@ -846,7 +846,7 @@ impl JsonManipulator {
}
pub fn remove_sub_node(&mut self, main_node: &str, name: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no node or empty node
let main_node_value = decoded.as_array().and_then(|a| a.get(main_node));
@@ -882,8 +882,8 @@ impl JsonManipulator {
let children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, true).is_null()
- || json_decode(&children, true).as_bool() == Some(false)
+ if json_decode(&children, true)?.is_null()
+ || json_decode(&children, true)?.as_bool() == Some(false)
{
return Ok(false);
}
@@ -926,11 +926,12 @@ impl JsonManipulator {
// try and find a match for the subkey
let key_regex = str_replace("/", "\\\\?/", &preg_quote(&name_owned, None));
let mut children_clean: Option<String> = None;
- if Preg::is_match(&format!("{{\"{}\"\\s*:}}i", key_regex), &children, None).unwrap_or(false)
+ if Preg::is_match3(&format!("{{\"{}\"\\s*:}}i", key_regex), &children, None)
+ .unwrap_or(false)
{
// find best match for the value of "name"
let mut all_matches: Vec<Vec<String>> = vec![];
- if Preg::is_match_all(
+ if Preg::is_match_all3(
&format!(
"{{{}\"{}\"\\s*:\\s*(?:(?&json))}}x",
Self::DEFINES,
@@ -1009,7 +1010,7 @@ impl JsonManipulator {
// we have a subname, so we restore the rest of $name
if let Some(sub) = sub_name {
- let mut cur_val = json_decode(&children, true);
+ let mut cur_val = json_decode(&children, true)?;
if let Some(arr) = cur_val.as_array_mut() {
if let Some(inner) = arr.get_mut(&name_owned).and_then(|v| v.as_array_mut())
{
@@ -1052,7 +1053,8 @@ impl JsonManipulator {
let mut children_clean = children_clean_capture.clone();
if let Some(ref sub) = sub_name_capture {
let mut cur_val =
- json_decode(matches.get("content").unwrap_or(&String::new()), true);
+ json_decode(matches.get("content").unwrap_or(&String::new()), true)
+ .unwrap_or(PhpMixed::Null);
if let Some(arr) = cur_val.as_array_mut() {
if let Some(inner) =
arr.get_mut(&name_capture).and_then(|v| v.as_array_mut())
@@ -1093,7 +1095,7 @@ impl JsonManipulator {
value: PhpMixed,
append: bool,
) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no main node yet
if decoded.as_array().and_then(|a| a.get(main_node)).is_none() {
@@ -1130,7 +1132,7 @@ impl JsonManipulator {
let mut children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, false).as_bool() == Some(false) {
+ if json_decode(&children, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1246,7 +1248,7 @@ impl JsonManipulator {
return self.add_list_item(main_node, value, false);
}
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no main node yet
if decoded.as_array().and_then(|a| a.get(main_node)).is_none() {
@@ -1293,7 +1295,7 @@ impl JsonManipulator {
let mut children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, false).as_bool() == Some(false) {
+ if json_decode(&children, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1348,7 +1350,7 @@ impl JsonManipulator {
return Ok(true);
}
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
// no node or empty node
let main_node_value = decoded.as_array().and_then(|a| a.get(main_node));
@@ -1384,7 +1386,7 @@ impl JsonManipulator {
let children = match_map.get("content").cloned().unwrap_or_default();
// invalid match due to un-regexable content, abort
- if json_decode(&children, true).as_bool() == Some(false) {
+ if json_decode(&children, true)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1447,7 +1449,7 @@ impl JsonManipulator {
}
pub fn add_main_key(&mut self, key: &str, content: PhpMixed) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let content = self.format(&content, 0, false)?;
// key exists already
@@ -1465,7 +1467,7 @@ impl JsonManipulator {
{
// invalid match due to un-regexable content, abort
let key_match = matches.get("key").cloned().unwrap_or_default();
- if json_decode(&format!("{{{}}}", key_match), false).is_null() {
+ if json_decode(&format!("{{{}}}", key_match), false)?.is_null() {
return Ok(false);
}
@@ -1482,7 +1484,7 @@ impl JsonManipulator {
// append at the end of the file and keep whitespace
let mut tail_match: Vec<String> = vec![];
- if Preg::is_match("#[^{\\s](\\s*)\\}$#", &self.contents, Some(&mut tail_match))
+ if Preg::is_match3("#[^{\\s](\\s*)\\}$#", &self.contents, Some(&mut tail_match))
.unwrap_or(false)
{
self.contents = Preg::replace(
@@ -1524,7 +1526,7 @@ impl JsonManipulator {
}
pub fn remove_main_key(&mut self, key: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let decoded_arr = decoded.as_array().cloned().unwrap_or_default();
if !array_key_exists(key, &decoded_arr) {
@@ -1544,21 +1546,21 @@ impl JsonManipulator {
if Preg::is_match_named(&regex, &self.contents, &mut matches).unwrap_or(false) {
// invalid match due to un-regexable content, abort
let removal = matches.get("removal").cloned().unwrap_or_default();
- if json_decode(&format!("{{{}}}", removal), false).is_null() {
+ if json_decode(&format!("{{{}}}", removal), false)?.is_null() {
return Ok(false);
}
// check that we are not leaving a dangling comma on the previous line if the last line was removed
let mut start = matches.get("start").cloned().unwrap_or_default();
let end = matches.get("end").cloned().unwrap_or_default();
- if Preg::is_match_strict_groups("#,\\s*$#", &start, None).unwrap_or(false)
- && Preg::is_match("#^\\}$#", &end, None).unwrap_or(false)
+ if Preg::is_match_strict_groups3("#,\\s*$#", &start, None).unwrap_or(false)
+ && Preg::is_match3("#^\\}$#", &end, None).unwrap_or(false)
{
start = rtrim(&Preg::replace("#,(\\s*)$#", "$1", &start), &self.indent);
}
self.contents = format!("{}{}", start, end);
- if Preg::is_match("#^\\{\\s*\\}\\s*$#", &self.contents, None).unwrap_or(false) {
+ if Preg::is_match3("#^\\{\\s*\\}\\s*$#", &self.contents, None).unwrap_or(false) {
self.contents = "{\n}".to_string();
}
@@ -1569,7 +1571,7 @@ impl JsonManipulator {
}
pub fn change_empty_main_key_from_assoc_to_list(&mut self, key: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let decoded_arr = decoded.as_array().cloned().unwrap_or_default();
if !array_key_exists(key, &decoded_arr) {
@@ -1588,7 +1590,7 @@ impl JsonManipulator {
if Preg::is_match_named(&regex, &self.contents, &mut matches).unwrap_or(false) {
// invalid match due to un-regexable content, abort
let removal = matches.get("removal").cloned().unwrap_or_default();
- if json_decode(&removal, false).as_bool() == Some(false) {
+ if json_decode(&removal, false)?.as_bool() == Some(false) {
return Ok(false);
}
@@ -1606,7 +1608,7 @@ impl JsonManipulator {
}
pub fn remove_main_key_if_empty(&mut self, key: &str) -> anyhow::Result<bool> {
- let decoded = JsonFile::parse_json(&self.contents, "composer.json")?;
+ let decoded = JsonFile::parse_json(Some(&self.contents), Some("composer.json"))?;
let decoded_arr = decoded.as_array().cloned().unwrap_or_default();
if !array_key_exists(key, &decoded_arr) {
@@ -1643,7 +1645,7 @@ impl JsonManipulator {
format!(
"{{{}{}}}",
self.newline,
- str_repeat(&self.indent, (depth + 1) as i64)
+ str_repeat(&self.indent, (depth + 1) as usize)
)
} else {
"[]".to_string()
@@ -1665,7 +1667,7 @@ impl JsonManipulator {
for (key, val) in arr {
elems.push(format!(
"{}{}: {}",
- str_repeat(&self.indent, (depth + 2) as i64),
+ str_repeat(&self.indent, (depth + 2) as usize),
JsonFile::encode(&PhpMixed::String(key.clone()), 0)?,
self.format(val, depth + 1, false)?
));
@@ -1677,7 +1679,7 @@ impl JsonManipulator {
out,
implode(&format!(",{}", self.newline), &elems),
self.newline,
- str_repeat(&self.indent, (depth + 1) as i64)
+ str_repeat(&self.indent, (depth + 1) as usize)
));
}
@@ -1714,7 +1716,7 @@ impl ManipulatorFormatter {
format!(
"{{{}{}}}",
self.newline,
- str_repeat(&self.indent, depth + 1)
+ str_repeat(&self.indent, (depth + 1) as usize)
)
} else {
"[]".to_string()
@@ -1736,7 +1738,7 @@ impl ManipulatorFormatter {
for (key, val) in arr {
elems.push(format!(
"{}{}: {}",
- str_repeat(&self.indent, depth + 2),
+ str_repeat(&self.indent, (depth + 2) as usize),
JsonFile::encode(&PhpMixed::String(key.clone()), 0)?,
self.format(val, depth + 1, false)?
));
@@ -1748,7 +1750,7 @@ impl ManipulatorFormatter {
out,
implode(&format!(",{}", self.newline), &elems),
self.newline,
- str_repeat(&self.indent, depth + 1)
+ str_repeat(&self.indent, (depth + 1) as usize)
));
}