conversions.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #pragma once
  8. #include <better/map.h>
  9. #include <folly/Conv.h>
  10. #include <folly/dynamic.h>
  11. #include <glog/logging.h>
  12. #include <react/components/view/primitives.h>
  13. #include <react/core/LayoutMetrics.h>
  14. #include <react/graphics/Geometry.h>
  15. #include <react/graphics/Transform.h>
  16. #include <stdlib.h>
  17. #include <yoga/YGEnums.h>
  18. #include <yoga/YGNode.h>
  19. #include <yoga/Yoga.h>
  20. #include <cmath>
  21. namespace facebook {
  22. namespace react {
  23. /*
  24. * Yoga's `float` <-> React Native's `Float` (can be `double` or `float`)
  25. *
  26. * Regular Yoga `float` values represent some onscreen-position-related values.
  27. * They can be real numbers or special value `YGUndefined` (which actually is
  28. * `NaN`). Conceptually, layout computation process inside Yoga should never
  29. * produce `NaN` values from non-`NaN` values. At the same time, ` YGUndefined`
  30. * values have special "no limit" meaning in Yoga, therefore ` YGUndefined`
  31. * usually corresponds to `Infinity` value.
  32. */
  33. inline Float floatFromYogaFloat(float value) {
  34. static_assert(
  35. YGUndefined != YGUndefined,
  36. "The code of this function assumes that YGUndefined is NaN.");
  37. if (std::isnan(value) /* means: `value == YGUndefined` */) {
  38. return std::numeric_limits<Float>::infinity();
  39. }
  40. return (Float)value;
  41. }
  42. inline float yogaFloatFromFloat(Float value) {
  43. if (std::isinf(value)) {
  44. return YGUndefined;
  45. }
  46. return (float)value;
  47. }
  48. /*
  49. * `YGFloatOptional` <-> React Native's `Float`
  50. *
  51. * `YGFloatOptional` represents optional dimensionless float values in Yoga
  52. * Style object (e.g. `flex`). The most suitable analogy to empty
  53. * `YGFloatOptional` is `NaN` value.
  54. * `YGFloatOptional` values are usually parsed from some outside data source
  55. * which usually has some special corresponding representation for an empty
  56. * value.
  57. */
  58. inline Float floatFromYogaOptionalFloat(YGFloatOptional value) {
  59. if (value.isUndefined()) {
  60. return std::numeric_limits<Float>::quiet_NaN();
  61. }
  62. return floatFromYogaFloat(value.unwrap());
  63. }
  64. inline YGFloatOptional yogaOptionalFloatFromFloat(Float value) {
  65. if (std::isnan(value)) {
  66. return YGFloatOptional();
  67. }
  68. return YGFloatOptional((float)value);
  69. }
  70. /*
  71. * `YGValue` <-> `React Native's `Float`
  72. *
  73. * `YGValue` represents optional dimensionful (a real number and some unit, e.g.
  74. * pixels).
  75. */
  76. inline YGValue yogaStyleValueFromFloat(
  77. const Float &value,
  78. YGUnit unit = YGUnitPoint) {
  79. if (std::isnan(value)) {
  80. return YGValueUndefined;
  81. }
  82. return {(float)value, unit};
  83. }
  84. inline folly::Optional<Float> optionalFloatFromYogaValue(
  85. const YGValue value,
  86. folly::Optional<Float> base = {}) {
  87. switch (value.unit) {
  88. case YGUnitUndefined:
  89. return {};
  90. case YGUnitPoint:
  91. return floatFromYogaFloat(value.value);
  92. case YGUnitPercent:
  93. return base.has_value()
  94. ? folly::Optional<Float>(
  95. base.value() * floatFromYogaFloat(value.value))
  96. : folly::Optional<Float>();
  97. case YGUnitAuto:
  98. return {};
  99. }
  100. }
  101. inline LayoutMetrics layoutMetricsFromYogaNode(YGNode &yogaNode) {
  102. auto layoutMetrics = LayoutMetrics{};
  103. layoutMetrics.frame =
  104. Rect{Point{floatFromYogaFloat(YGNodeLayoutGetLeft(&yogaNode)),
  105. floatFromYogaFloat(YGNodeLayoutGetTop(&yogaNode))},
  106. Size{floatFromYogaFloat(YGNodeLayoutGetWidth(&yogaNode)),
  107. floatFromYogaFloat(YGNodeLayoutGetHeight(&yogaNode))}};
  108. layoutMetrics.borderWidth = EdgeInsets{
  109. floatFromYogaFloat(YGNodeLayoutGetBorder(&yogaNode, YGEdgeLeft)),
  110. floatFromYogaFloat(YGNodeLayoutGetBorder(&yogaNode, YGEdgeTop)),
  111. floatFromYogaFloat(YGNodeLayoutGetBorder(&yogaNode, YGEdgeRight)),
  112. floatFromYogaFloat(YGNodeLayoutGetBorder(&yogaNode, YGEdgeBottom))};
  113. layoutMetrics.contentInsets = EdgeInsets{
  114. layoutMetrics.borderWidth.left +
  115. floatFromYogaFloat(YGNodeLayoutGetPadding(&yogaNode, YGEdgeLeft)),
  116. layoutMetrics.borderWidth.top +
  117. floatFromYogaFloat(YGNodeLayoutGetPadding(&yogaNode, YGEdgeTop)),
  118. layoutMetrics.borderWidth.right +
  119. floatFromYogaFloat(YGNodeLayoutGetPadding(&yogaNode, YGEdgeRight)),
  120. layoutMetrics.borderWidth.bottom +
  121. floatFromYogaFloat(YGNodeLayoutGetPadding(&yogaNode, YGEdgeBottom))};
  122. layoutMetrics.displayType = yogaNode.getStyle().display() == YGDisplayNone
  123. ? DisplayType::None
  124. : DisplayType::Flex;
  125. layoutMetrics.layoutDirection =
  126. YGNodeLayoutGetDirection(&yogaNode) == YGDirectionRTL
  127. ? LayoutDirection::RightToLeft
  128. : LayoutDirection::LeftToRight;
  129. return layoutMetrics;
  130. }
  131. inline YGDirection yogaDirectionFromLayoutDirection(LayoutDirection direction) {
  132. switch (direction) {
  133. case LayoutDirection::Undefined:
  134. return YGDirectionInherit;
  135. case LayoutDirection::LeftToRight:
  136. return YGDirectionLTR;
  137. case LayoutDirection::RightToLeft:
  138. return YGDirectionRTL;
  139. }
  140. }
  141. inline void fromRawValue(const RawValue &value, YGDirection &result) {
  142. assert(value.hasType<std::string>());
  143. auto stringValue = (std::string)value;
  144. if (stringValue == "inherit") {
  145. result = YGDirectionInherit;
  146. return;
  147. }
  148. if (stringValue == "ltr") {
  149. result = YGDirectionLTR;
  150. return;
  151. }
  152. if (stringValue == "rtl") {
  153. result = YGDirectionRTL;
  154. return;
  155. }
  156. LOG(FATAL) << "Could not parse YGDirection:" << stringValue;
  157. assert(false);
  158. }
  159. inline void fromRawValue(const RawValue &value, YGFlexDirection &result) {
  160. assert(value.hasType<std::string>());
  161. auto stringValue = (std::string)value;
  162. if (stringValue == "row") {
  163. result = YGFlexDirectionRow;
  164. return;
  165. }
  166. if (stringValue == "column") {
  167. result = YGFlexDirectionColumn;
  168. return;
  169. }
  170. if (stringValue == "column-reverse") {
  171. result = YGFlexDirectionColumnReverse;
  172. return;
  173. }
  174. if (stringValue == "row-reverse") {
  175. result = YGFlexDirectionRowReverse;
  176. return;
  177. }
  178. LOG(FATAL) << "Could not parse YGFlexDirection:" << stringValue;
  179. assert(false);
  180. }
  181. inline void fromRawValue(const RawValue &value, YGJustify &result) {
  182. assert(value.hasType<std::string>());
  183. auto stringValue = (std::string)value;
  184. if (stringValue == "flex-start") {
  185. result = YGJustifyFlexStart;
  186. return;
  187. }
  188. if (stringValue == "center") {
  189. result = YGJustifyCenter;
  190. return;
  191. }
  192. if (stringValue == "flex-end") {
  193. result = YGJustifyFlexEnd;
  194. return;
  195. }
  196. if (stringValue == "space-between") {
  197. result = YGJustifySpaceBetween;
  198. return;
  199. }
  200. if (stringValue == "space-around") {
  201. result = YGJustifySpaceAround;
  202. return;
  203. }
  204. if (stringValue == "space-evenly") {
  205. result = YGJustifySpaceEvenly;
  206. return;
  207. }
  208. LOG(FATAL) << "Could not parse YGJustify:" << stringValue;
  209. assert(false);
  210. }
  211. inline void fromRawValue(const RawValue &value, YGAlign &result) {
  212. assert(value.hasType<std::string>());
  213. auto stringValue = (std::string)value;
  214. if (stringValue == "auto") {
  215. result = YGAlignAuto;
  216. return;
  217. }
  218. if (stringValue == "flex-start") {
  219. result = YGAlignFlexStart;
  220. return;
  221. }
  222. if (stringValue == "center") {
  223. result = YGAlignCenter;
  224. return;
  225. }
  226. if (stringValue == "flex-end") {
  227. result = YGAlignFlexEnd;
  228. return;
  229. }
  230. if (stringValue == "stretch") {
  231. result = YGAlignStretch;
  232. return;
  233. }
  234. if (stringValue == "baseline") {
  235. result = YGAlignBaseline;
  236. return;
  237. }
  238. if (stringValue == "between") {
  239. result = YGAlignSpaceBetween;
  240. return;
  241. }
  242. if (stringValue == "space-around") {
  243. result = YGAlignSpaceAround;
  244. return;
  245. }
  246. LOG(FATAL) << "Could not parse YGAlign:" << stringValue;
  247. assert(false);
  248. }
  249. inline void fromRawValue(const RawValue &value, YGPositionType &result) {
  250. assert(value.hasType<std::string>());
  251. auto stringValue = (std::string)value;
  252. if (stringValue == "relative") {
  253. result = YGPositionTypeRelative;
  254. return;
  255. }
  256. if (stringValue == "absolute") {
  257. result = YGPositionTypeAbsolute;
  258. return;
  259. }
  260. LOG(FATAL) << "Could not parse YGPositionType:" << stringValue;
  261. assert(false);
  262. }
  263. inline void fromRawValue(const RawValue &value, YGWrap &result) {
  264. assert(value.hasType<std::string>());
  265. auto stringValue = (std::string)value;
  266. if (stringValue == "nowrap") {
  267. result = YGWrapNoWrap;
  268. return;
  269. }
  270. if (stringValue == "wrap") {
  271. result = YGWrapWrap;
  272. return;
  273. }
  274. if (stringValue == "wrap-reverse") {
  275. result = YGWrapWrapReverse;
  276. return;
  277. }
  278. LOG(FATAL) << "Could not parse YGWrap:" << stringValue;
  279. assert(false);
  280. }
  281. inline void fromRawValue(const RawValue &value, YGOverflow &result) {
  282. assert(value.hasType<std::string>());
  283. auto stringValue = (std::string)value;
  284. if (stringValue == "visible") {
  285. result = YGOverflowVisible;
  286. return;
  287. }
  288. if (stringValue == "hidden") {
  289. result = YGOverflowHidden;
  290. return;
  291. }
  292. if (stringValue == "scroll") {
  293. result = YGOverflowScroll;
  294. return;
  295. }
  296. LOG(FATAL) << "Could not parse YGOverflow:" << stringValue;
  297. assert(false);
  298. }
  299. inline void fromRawValue(const RawValue &value, YGDisplay &result) {
  300. assert(value.hasType<std::string>());
  301. auto stringValue = (std::string)value;
  302. if (stringValue == "flex") {
  303. result = YGDisplayFlex;
  304. return;
  305. }
  306. if (stringValue == "none") {
  307. result = YGDisplayNone;
  308. return;
  309. }
  310. LOG(FATAL) << "Could not parse YGDisplay:" << stringValue;
  311. assert(false);
  312. }
  313. inline void fromRawValue(const RawValue &value, YGStyle::ValueRepr &result) {
  314. if (value.hasType<Float>()) {
  315. result = yogaStyleValueFromFloat((Float)value);
  316. return;
  317. } else if (value.hasType<std::string>()) {
  318. const auto stringValue = (std::string)value;
  319. if (stringValue == "auto") {
  320. result = YGValueUndefined;
  321. return;
  322. } else {
  323. if (stringValue.back() == '%') {
  324. result = YGValue{
  325. folly::to<float>(stringValue.substr(0, stringValue.length() - 1)),
  326. YGUnitPercent};
  327. return;
  328. } else {
  329. result = YGValue{folly::to<float>(stringValue), YGUnitPoint};
  330. return;
  331. }
  332. }
  333. }
  334. result = YGValueUndefined;
  335. }
  336. inline void fromRawValue(const RawValue &value, YGFloatOptional &result) {
  337. if (value.hasType<float>()) {
  338. result = YGFloatOptional((float)value);
  339. return;
  340. } else if (value.hasType<std::string>()) {
  341. const auto stringValue = (std::string)value;
  342. if (stringValue == "auto") {
  343. result = YGFloatOptional();
  344. return;
  345. }
  346. }
  347. LOG(FATAL) << "Could not parse YGFloatOptional";
  348. assert(false);
  349. }
  350. inline Float toRadians(const RawValue &value) {
  351. if (value.hasType<Float>()) {
  352. return (Float)value;
  353. }
  354. assert(value.hasType<std::string>());
  355. auto stringValue = (std::string)value;
  356. char *suffixStart;
  357. double num = strtod(
  358. stringValue.c_str(), &suffixStart); // can't use std::stod, probably
  359. // because of old Android NDKs
  360. if (0 == strncmp(suffixStart, "deg", 3)) {
  361. return num * M_PI / 180;
  362. }
  363. return num; // assume suffix is "rad"
  364. }
  365. inline void fromRawValue(const RawValue &value, Transform &result) {
  366. assert(value.hasType<std::vector<RawValue>>());
  367. auto transformMatrix = Transform{};
  368. auto configurations = static_cast<std::vector<RawValue>>(value);
  369. for (const auto &configuration : configurations) {
  370. if (!configuration.hasType<better::map<std::string, RawValue>>()) {
  371. // TODO: The following checks have to be removed after codegen is shipped.
  372. // See T45151459.
  373. continue;
  374. }
  375. auto configurationPair =
  376. static_cast<better::map<std::string, RawValue>>(configuration);
  377. auto pair = configurationPair.begin();
  378. auto operation = pair->first;
  379. auto &parameters = pair->second;
  380. if (operation == "matrix") {
  381. assert(parameters.hasType<std::vector<Float>>());
  382. auto numbers = (std::vector<Float>)parameters;
  383. assert(numbers.size() == transformMatrix.matrix.size());
  384. auto i = 0;
  385. for (auto number : numbers) {
  386. transformMatrix.matrix[i++] = number;
  387. }
  388. } else if (operation == "perspective") {
  389. transformMatrix =
  390. transformMatrix * Transform::Perspective((Float)parameters);
  391. } else if (operation == "rotateX") {
  392. transformMatrix =
  393. transformMatrix * Transform::Rotate(toRadians(parameters), 0, 0);
  394. } else if (operation == "rotateY") {
  395. transformMatrix =
  396. transformMatrix * Transform::Rotate(0, toRadians(parameters), 0);
  397. } else if (operation == "rotateZ" || operation == "rotate") {
  398. transformMatrix =
  399. transformMatrix * Transform::Rotate(0, 0, toRadians(parameters));
  400. } else if (operation == "scale") {
  401. auto number = (Float)parameters;
  402. transformMatrix =
  403. transformMatrix * Transform::Scale(number, number, number);
  404. } else if (operation == "scaleX") {
  405. transformMatrix =
  406. transformMatrix * Transform::Scale((Float)parameters, 1, 1);
  407. } else if (operation == "scaleY") {
  408. transformMatrix =
  409. transformMatrix * Transform::Scale(1, (Float)parameters, 1);
  410. } else if (operation == "scaleZ") {
  411. transformMatrix =
  412. transformMatrix * Transform::Scale(1, 1, (Float)parameters);
  413. } else if (operation == "translate") {
  414. auto numbers = (std::vector<Float>)parameters;
  415. transformMatrix = transformMatrix *
  416. Transform::Translate(numbers.at(0), numbers.at(1), 0);
  417. } else if (operation == "translateX") {
  418. transformMatrix =
  419. transformMatrix * Transform::Translate((Float)parameters, 0, 0);
  420. } else if (operation == "translateY") {
  421. transformMatrix =
  422. transformMatrix * Transform::Translate(0, (Float)parameters, 0);
  423. } else if (operation == "skewX") {
  424. transformMatrix =
  425. transformMatrix * Transform::Skew(toRadians(parameters), 0);
  426. } else if (operation == "skewY") {
  427. transformMatrix =
  428. transformMatrix * Transform::Skew(0, toRadians(parameters));
  429. }
  430. }
  431. result = transformMatrix;
  432. }
  433. inline void fromRawValue(const RawValue &value, PointerEventsMode &result) {
  434. assert(value.hasType<std::string>());
  435. auto stringValue = (std::string)value;
  436. if (stringValue == "auto") {
  437. result = PointerEventsMode::Auto;
  438. return;
  439. }
  440. if (stringValue == "none") {
  441. result = PointerEventsMode::None;
  442. return;
  443. }
  444. if (stringValue == "box-none") {
  445. result = PointerEventsMode::BoxNone;
  446. return;
  447. }
  448. if (stringValue == "box-only") {
  449. result = PointerEventsMode::BoxOnly;
  450. return;
  451. }
  452. LOG(FATAL) << "Could not parse PointerEventsMode:" << stringValue;
  453. assert(false);
  454. }
  455. inline void fromRawValue(const RawValue &value, BackfaceVisibility &result) {
  456. assert(value.hasType<std::string>());
  457. auto stringValue = (std::string)value;
  458. if (stringValue == "auto") {
  459. result = BackfaceVisibility::Auto;
  460. return;
  461. }
  462. if (stringValue == "visible") {
  463. result = BackfaceVisibility::Visible;
  464. return;
  465. }
  466. if (stringValue == "hidden") {
  467. result = BackfaceVisibility::Hidden;
  468. return;
  469. }
  470. LOG(FATAL) << "Could not parse BackfaceVisibility:" << stringValue;
  471. assert(false);
  472. }
  473. inline void fromRawValue(const RawValue &value, BorderStyle &result) {
  474. assert(value.hasType<std::string>());
  475. auto stringValue = (std::string)value;
  476. if (stringValue == "solid") {
  477. result = BorderStyle::Solid;
  478. return;
  479. }
  480. if (stringValue == "dotted") {
  481. result = BorderStyle::Dotted;
  482. return;
  483. }
  484. if (stringValue == "dashed") {
  485. result = BorderStyle::Dashed;
  486. return;
  487. }
  488. LOG(FATAL) << "Could not parse BorderStyle:" << stringValue;
  489. assert(false);
  490. }
  491. inline std::string toString(
  492. const std::array<float, yoga::enums::count<YGDimension>()> &dimensions) {
  493. return "{" + folly::to<std::string>(dimensions[0]) + ", " +
  494. folly::to<std::string>(dimensions[1]) + "}";
  495. }
  496. inline std::string toString(const std::array<float, 4> &position) {
  497. return "{" + folly::to<std::string>(position[0]) + ", " +
  498. folly::to<std::string>(position[1]) + "}";
  499. }
  500. inline std::string toString(
  501. const std::array<float, yoga::enums::count<YGEdge>()> &edges) {
  502. return "{" + folly::to<std::string>(edges[0]) + ", " +
  503. folly::to<std::string>(edges[1]) + ", " +
  504. folly::to<std::string>(edges[2]) + ", " +
  505. folly::to<std::string>(edges[3]) + "}";
  506. }
  507. inline std::string toString(const YGDirection &value) {
  508. switch (value) {
  509. case YGDirectionInherit:
  510. return "inherit";
  511. case YGDirectionLTR:
  512. return "ltr";
  513. case YGDirectionRTL:
  514. return "rtl";
  515. }
  516. }
  517. inline std::string toString(const YGFlexDirection &value) {
  518. switch (value) {
  519. case YGFlexDirectionColumn:
  520. return "column";
  521. case YGFlexDirectionColumnReverse:
  522. return "column-reverse";
  523. case YGFlexDirectionRow:
  524. return "row";
  525. case YGFlexDirectionRowReverse:
  526. return "row-reverse";
  527. }
  528. }
  529. inline std::string toString(const YGJustify &value) {
  530. switch (value) {
  531. case YGJustifyFlexStart:
  532. return "flex-start";
  533. case YGJustifyCenter:
  534. return "center";
  535. case YGJustifyFlexEnd:
  536. return "flex-end";
  537. case YGJustifySpaceBetween:
  538. return "space-between";
  539. case YGJustifySpaceAround:
  540. return "space-around";
  541. case YGJustifySpaceEvenly:
  542. return "space-evenly";
  543. }
  544. }
  545. inline std::string toString(const YGAlign &value) {
  546. switch (value) {
  547. case YGAlignAuto:
  548. return "auto";
  549. case YGAlignFlexStart:
  550. return "flex-start";
  551. case YGAlignCenter:
  552. return "center";
  553. case YGAlignFlexEnd:
  554. return "flex-end";
  555. case YGAlignStretch:
  556. return "stretch";
  557. case YGAlignBaseline:
  558. return "baseline";
  559. case YGAlignSpaceBetween:
  560. return "space-between";
  561. case YGAlignSpaceAround:
  562. return "space-around";
  563. }
  564. }
  565. inline std::string toString(const YGPositionType &value) {
  566. switch (value) {
  567. case YGPositionTypeRelative:
  568. return "relative";
  569. case YGPositionTypeAbsolute:
  570. return "absolute";
  571. }
  572. }
  573. inline std::string toString(const YGWrap &value) {
  574. switch (value) {
  575. case YGWrapNoWrap:
  576. return "no-wrap";
  577. case YGWrapWrap:
  578. return "wrap";
  579. case YGWrapWrapReverse:
  580. return "wrap-reverse";
  581. }
  582. }
  583. inline std::string toString(const YGOverflow &value) {
  584. switch (value) {
  585. case YGOverflowVisible:
  586. return "visible";
  587. case YGOverflowScroll:
  588. return "scroll";
  589. case YGOverflowHidden:
  590. return "hidden";
  591. }
  592. }
  593. inline std::string toString(const YGDisplay &value) {
  594. switch (value) {
  595. case YGDisplayFlex:
  596. return "flex";
  597. case YGDisplayNone:
  598. return "none";
  599. }
  600. }
  601. inline std::string toString(const YGValue &value) {
  602. switch (value.unit) {
  603. case YGUnitUndefined:
  604. return "undefined";
  605. case YGUnitPoint:
  606. return folly::to<std::string>(value.value);
  607. case YGUnitPercent:
  608. return folly::to<std::string>(value.value) + "%";
  609. case YGUnitAuto:
  610. return "auto";
  611. }
  612. }
  613. inline std::string toString(const YGFloatOptional &value) {
  614. if (value.isUndefined()) {
  615. return "undefined";
  616. }
  617. return folly::to<std::string>(floatFromYogaFloat(value.unwrap()));
  618. }
  619. inline std::string toString(const YGStyle::Dimensions &value) {
  620. return "{" + toString(value[0]) + ", " + toString(value[1]) + "}";
  621. }
  622. inline std::string toString(const YGStyle::Edges &value) {
  623. static std::array<std::string, yoga::enums::count<YGEdge>()> names = {
  624. {"left",
  625. "top",
  626. "right",
  627. "bottom",
  628. "start",
  629. "end",
  630. "horizontal",
  631. "vertical",
  632. "all"}};
  633. auto result = std::string{};
  634. auto separator = std::string{", "};
  635. for (auto i = 0; i < yoga::enums::count<YGEdge>(); i++) {
  636. YGValue v = value[i];
  637. if (v.unit == YGUnitUndefined) {
  638. continue;
  639. }
  640. result += names[i] + ": " + toString(v) + separator;
  641. }
  642. if (!result.empty()) {
  643. result.erase(result.length() - separator.length());
  644. }
  645. return "{" + result + "}";
  646. }
  647. } // namespace react
  648. } // namespace facebook