|
1 | 1 | use clarity_repl::clarity::coverage::CoverageReporter; |
| 2 | +use clarity_repl::clarity::types; |
| 3 | +use clarity_repl::clarity::util::hash; |
2 | 4 | use clarity_repl::prettytable::{color, format, Attr, Cell, Row, Table}; |
3 | 5 | use clarity_repl::repl::session::CostsReport; |
4 | 6 | use clarity_repl::repl::Session; |
@@ -35,6 +37,7 @@ use serde::Serialize; |
35 | 37 | use std::collections::HashSet; |
36 | 38 | use std::collections::{btree_map::Entry, BTreeMap}; |
37 | 39 | use std::convert::TryFrom; |
| 40 | +use std::fmt::Write; |
38 | 41 | use std::ops::Index; |
39 | 42 | use std::path::Path; |
40 | 43 | use std::path::PathBuf; |
@@ -1183,6 +1186,61 @@ struct TransferSTXArgs { |
1183 | 1186 | recipient: String, |
1184 | 1187 | } |
1185 | 1188 |
|
| 1189 | +fn value_to_string(value: &types::Value) -> String { |
| 1190 | + use clarity_repl::clarity::types::{CharType, SequenceData, Value}; |
| 1191 | + |
| 1192 | + match value { |
| 1193 | + Value::Tuple(tup_data) => { |
| 1194 | + let mut out = String::new(); |
| 1195 | + write!(out, "{{"); |
| 1196 | + for (i, (name, value)) in tup_data.data_map.iter().enumerate() { |
| 1197 | + write!(out, "{}: {}", &**name, value_to_string(value)); |
| 1198 | + if i < tup_data.data_map.len() - 1 { |
| 1199 | + write!(out, ", "); |
| 1200 | + } |
| 1201 | + } |
| 1202 | + write!(out, "}}"); |
| 1203 | + out |
| 1204 | + } |
| 1205 | + Value::Optional(opt_data) => match opt_data.data { |
| 1206 | + Some(ref x) => format!("(some {})", value_to_string(&**x)), |
| 1207 | + None => "none".to_string(), |
| 1208 | + }, |
| 1209 | + Value::Response(res_data) => match res_data.committed { |
| 1210 | + true => format!("(ok {})", value_to_string(&*res_data.data)), |
| 1211 | + false => format!("(err {})", value_to_string(&*res_data.data)), |
| 1212 | + }, |
| 1213 | + Value::Sequence(SequenceData::String(CharType::ASCII(data))) => { |
| 1214 | + format!("\"{}\"", String::from_utf8(data.data.clone()).unwrap()) |
| 1215 | + } |
| 1216 | + Value::Sequence(SequenceData::String(CharType::UTF8(data))) => { |
| 1217 | + let mut result = String::new(); |
| 1218 | + for c in data.data.iter() { |
| 1219 | + if c.len() > 1 { |
| 1220 | + // We escape extended charset |
| 1221 | + result.push_str(&format!("\\u{{{}}}", hash::to_hex(&c[..]))); |
| 1222 | + } else { |
| 1223 | + result.push(c[0] as char) |
| 1224 | + } |
| 1225 | + } |
| 1226 | + format!("u\"{}\"", result) |
| 1227 | + } |
| 1228 | + Value::Sequence(SequenceData::List(list_data)) => { |
| 1229 | + let mut out = String::new(); |
| 1230 | + write!(out, "["); |
| 1231 | + for (ix, v) in list_data.data.iter().enumerate() { |
| 1232 | + if ix > 0 { |
| 1233 | + write!(out, ", "); |
| 1234 | + } |
| 1235 | + write!(out, "{}", value_to_string(v)); |
| 1236 | + } |
| 1237 | + write!(out, "]"); |
| 1238 | + out |
| 1239 | + } |
| 1240 | + _ => format!("{}", value), |
| 1241 | + } |
| 1242 | +} |
| 1243 | + |
1186 | 1244 | fn mine_block(state: &mut OpState, args: Value, _: ()) -> Result<String, AnyError> { |
1187 | 1245 | let args: MineBlockArgs = |
1188 | 1246 | serde_json::from_value(args).expect("Invalid request from JavaScript."); |
@@ -1215,7 +1273,7 @@ fn mine_block(state: &mut OpState, args: Value, _: ()) -> Result<String, AnyErro |
1215 | 1273 | } |
1216 | 1274 | }; |
1217 | 1275 | let result = match execution.result { |
1218 | | - Some(output) => format!("{}", output), |
| 1276 | + Some(output) => value_to_string(&output), |
1219 | 1277 | _ => unreachable!("Value empty"), |
1220 | 1278 | }; |
1221 | 1279 | receipts.push((result, execution.events)); |
@@ -1360,3 +1418,84 @@ fn get_assets_maps(state: &mut OpState, args: Value, _: ()) -> Result<String, An |
1360 | 1418 | }) |
1361 | 1419 | .to_string()) |
1362 | 1420 | } |
| 1421 | + |
| 1422 | +#[cfg(test)] |
| 1423 | +mod tests { |
| 1424 | + use clarity_repl::clarity::representations::ClarityName; |
| 1425 | + use clarity_repl::clarity::types::{ |
| 1426 | + ListTypeData, OptionalData, ResponseData, SequenceData, SequencedValue, TupleData, |
| 1427 | + }; |
| 1428 | + |
| 1429 | + use super::*; |
| 1430 | + |
| 1431 | + #[test] |
| 1432 | + fn test_value_to_string() { |
| 1433 | + let mut s = value_to_string(&types::Value::Int(42)); |
| 1434 | + assert_eq!(s, "42"); |
| 1435 | + |
| 1436 | + s = value_to_string(&types::Value::UInt(12345678909876)); |
| 1437 | + assert_eq!(s, "u12345678909876"); |
| 1438 | + |
| 1439 | + s = value_to_string(&types::Value::Bool(true)); |
| 1440 | + assert_eq!(s, "true"); |
| 1441 | + |
| 1442 | + s = value_to_string(&types::Value::buff_from(vec![1, 2, 3]).unwrap()); |
| 1443 | + assert_eq!(s, "0x010203"); |
| 1444 | + |
| 1445 | + s = value_to_string(&types::Value::buff_from(vec![1, 2, 3]).unwrap()); |
| 1446 | + assert_eq!(s, "0x010203"); |
| 1447 | + |
| 1448 | + s = value_to_string(&types::Value::Tuple( |
| 1449 | + TupleData::from_data(vec![( |
| 1450 | + ClarityName::try_from("foo".to_string()).unwrap(), |
| 1451 | + types::Value::Bool(true), |
| 1452 | + )]) |
| 1453 | + .unwrap(), |
| 1454 | + )); |
| 1455 | + assert_eq!(s, "{foo: true}"); |
| 1456 | + |
| 1457 | + s = value_to_string(&types::Value::Optional(OptionalData { |
| 1458 | + data: Some(Box::new(types::Value::UInt(42))), |
| 1459 | + })); |
| 1460 | + assert_eq!(s, "(some u42)"); |
| 1461 | + |
| 1462 | + s = value_to_string(&types::NONE); |
| 1463 | + assert_eq!(s, "none"); |
| 1464 | + |
| 1465 | + s = value_to_string(&types::Value::Response(ResponseData { |
| 1466 | + committed: true, |
| 1467 | + data: Box::new(types::Value::Int(-321)), |
| 1468 | + })); |
| 1469 | + assert_eq!(s, "(ok -321)"); |
| 1470 | + |
| 1471 | + s = value_to_string(&types::Value::Response(ResponseData { |
| 1472 | + committed: false, |
| 1473 | + data: Box::new(types::Value::Sequence(types::SequenceData::String( |
| 1474 | + types::CharType::ASCII(types::ASCIIData { |
| 1475 | + data: "'foo'".as_bytes().to_vec(), |
| 1476 | + }), |
| 1477 | + ))), |
| 1478 | + })); |
| 1479 | + assert_eq!(s, "(err \"'foo'\")"); |
| 1480 | + |
| 1481 | + s = value_to_string(&types::Value::Sequence(types::SequenceData::String( |
| 1482 | + types::CharType::ASCII(types::ASCIIData { |
| 1483 | + data: "Hello, \"world\"\n".as_bytes().to_vec(), |
| 1484 | + }), |
| 1485 | + ))); |
| 1486 | + assert_eq!(s, "\"Hello, \"world\"\n\""); |
| 1487 | + |
| 1488 | + s = value_to_string(&types::UTF8Data::to_value( |
| 1489 | + &"Hello, 'world'\n".as_bytes().to_vec(), |
| 1490 | + )); |
| 1491 | + assert_eq!(s, "u\"Hello, 'world'\n\""); |
| 1492 | + |
| 1493 | + s = value_to_string(&types::Value::Sequence(SequenceData::List( |
| 1494 | + types::ListData { |
| 1495 | + data: vec![types::Value::Int(-321)], |
| 1496 | + type_signature: ListTypeData::new_list(types::TypeSignature::IntType, 2).unwrap(), |
| 1497 | + }, |
| 1498 | + ))); |
| 1499 | + assert_eq!(s, "[-321]"); |
| 1500 | + } |
| 1501 | +} |
0 commit comments