Skip to content
Open
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
49 changes: 18 additions & 31 deletions src/cmds/cloud/curl_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,35 +54,30 @@ pub fn run(args: &[String], verbose: u8) -> Result<i32> {
fn filter_curl_output(output: &str) -> String {
let trimmed = output.trim();

// Try JSON detection: starts with { or [
// JSON output: pass through unchanged to preserve validity for piping (#1015)
if (trimmed.starts_with('{') || trimmed.starts_with('['))
&& (trimmed.ends_with('}') || trimmed.ends_with(']'))
{
if let Ok(schema) = json_cmd::filter_json_string(trimmed, 5) {
// Only use schema if it's actually shorter than the original (#297)
if schema.len() <= trimmed.len() {
return schema;
}
}
return trimmed.to_string();
}

// Not JSON: truncate long output
// Non-JSON: truncate long output
let lines: Vec<&str> = trimmed.lines().collect();
if lines.len() > 30 {
let mut result: Vec<&str> = lines[..30].to_vec();
if lines.len() > 50 {
let mut result: Vec<&str> = lines[..50].to_vec();
result.push("");
let msg = format!(
"... ({} more lines, {} bytes total)",
lines.len() - 30,
lines.len() - 50,
trimmed.len()
);
return format!("{}\n{}", result.join("\n"), msg);
}

// Short output: return as-is but truncate long lines
// Short non-JSON output: truncate long lines
lines
.iter()
.map(|l| truncate(l, 200))
.map(|l| truncate(l, 300))
.collect::<Vec<_>>()
.join("\n")
}
Expand All @@ -92,20 +87,15 @@ mod tests {
use super::*;

#[test]
fn test_filter_curl_json() {
// Large JSON where schema is shorter than original — schema should be returned
let output = r#"{"name": "a very long user name here", "count": 42, "items": [1, 2, 3], "description": "a very long description that takes up many characters in the original JSON payload", "status": "active", "url": "https://example.com/api/v1/users/123"}"#;
fn test_filter_curl_json_preserves_valid_json() {
// curl output must remain valid JSON for downstream parsers (#1015)
let output = r#"{"name": "test", "count": 42, "items": [1, 2, 3]}"#;
let result = filter_curl_output(output);
assert!(result.contains("name"));
assert!(result.contains("string"));
assert!(result.contains("int"));
}

#[test]
fn test_filter_curl_json_array() {
let output = r#"[{"id": 1}, {"id": 2}]"#;
let result = filter_curl_output(output);
assert!(result.contains("id"));
assert!(result.contains("\"name\""));
assert!(result.contains("42"));
// Must be parseable as JSON
assert!(serde_json::from_str::<serde_json::Value>(&result).is_ok(),
"curl output must be valid JSON: {}", result);
}

#[test]
Expand All @@ -118,21 +108,18 @@ mod tests {

#[test]
fn test_filter_curl_json_small_returns_original() {
// Small JSON where schema would be larger than original (issue #297)
let output = r#"{"r2Ready":true,"status":"ok"}"#;
let result = filter_curl_output(output);
// Schema would be "{\n r2Ready: bool,\n status: string\n}" which is longer
// Should return the original JSON unchanged
assert_eq!(result.trim(), output.trim());
}

#[test]
fn test_filter_curl_long_output() {
let lines: Vec<String> = (0..50).map(|i| format!("Line {}", i)).collect();
let lines: Vec<String> = (0..80).map(|i| format!("Line {}", i)).collect();
let output = lines.join("\n");
let result = filter_curl_output(&output);
assert!(result.contains("Line 0"));
assert!(result.contains("Line 29"));
assert!(result.contains("Line 49"));
assert!(result.contains("more lines"));
}
}
24 changes: 19 additions & 5 deletions src/cmds/system/json_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,12 @@ fn extract_schema(value: &Value, depth: usize, max_depth: usize) -> String {

if is_simple {
if i < keys.len() - 1 {
lines.push(format!("{} {}: {},", indent, key, val_trimmed));
lines.push(format!("{} \"{}\": {},", indent, key, val_trimmed));
} else {
lines.push(format!("{} {}: {}", indent, key, val_trimmed));
lines.push(format!("{} \"{}\": {}", indent, key, val_trimmed));
}
} else {
lines.push(format!("{} {}:", indent, key));
lines.push(format!("{} \"{}\":", indent, key));
lines.push(val_schema);
}

Expand Down Expand Up @@ -316,16 +316,30 @@ mod tests {
fn test_extract_schema_simple() {
let json: Value = serde_json::from_str(r#"{"name": "test", "count": 42}"#).unwrap();
let schema = extract_schema(&json, 0, 5);
assert!(schema.contains("name"));
assert!(schema.contains("\"name\""));
assert!(schema.contains("string"));
assert!(schema.contains("int"));
}

#[test]
fn test_extract_schema_keys_are_quoted() {
let json: Value =
serde_json::from_str(r#"{"id": 1, "status": "open", "nested": {"key": "val"}}"#)
.unwrap();
let schema = extract_schema(&json, 0, 5);
assert!(schema.contains("\"id\":"), "keys must be double-quoted: {}", schema);
assert!(schema.contains("\"status\":"), "keys must be double-quoted: {}", schema);
assert!(schema.contains("\"nested\":"), "keys must be double-quoted: {}", schema);
assert!(schema.contains("\"key\":"), "nested keys must be double-quoted: {}", schema);
// Verify the output is parseable as valid JSON (values replaced with types)
// At minimum, keys should be properly quoted
}

#[test]
fn test_extract_schema_array() {
let json: Value = serde_json::from_str(r#"{"items": [1, 2, 3]}"#).unwrap();
let schema = extract_schema(&json, 0, 5);
assert!(schema.contains("items"));
assert!(schema.contains("\"items\""));
assert!(schema.contains("(3)"));
}
}
Loading