diff --git a/Cargo.lock b/Cargo.lock index 7bda24ae8..b71023a6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,7 +400,7 @@ version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.39", @@ -721,12 +721,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "data-encoding" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - [[package]] name = "der" version = "0.7.8" @@ -928,18 +922,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum-as-inner" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.39", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -968,15 +950,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" -[[package]] -name = "faster-hex" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" -dependencies = [ - "serde", -] - [[package]] name = "faster-hex" version = "0.9.0" @@ -1156,12 +1129,13 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gix" -version = "0.55.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002667cd1ebb789313d0d0afe3d23b2821cf3b0e91605095f0e6d8751f0ceeea" +checksum = "31887c304d9a935f3e5494fb5d6a0106c34e965168ec0db9b457424eedd0c741" dependencies = [ "gix-actor", "gix-attributes", + "gix-command", "gix-commitgraph", "gix-config", "gix-credentials", @@ -1206,14 +1180,13 @@ dependencies = [ "reqwest", "smallvec", "thiserror", - "unicode-normalization", ] [[package]] name = "gix-actor" -version = "0.28.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadca029ef716b4378f7afb19f7ee101fde9e58ba1f1445971315ac866db417" +checksum = "0a7bb9fad6125c81372987c06469601d37e1a2d421511adb69971b9083517a8a" dependencies = [ "bstr", "btoi", @@ -1225,9 +1198,9 @@ dependencies = [ [[package]] name = "gix-attributes" -version = "0.20.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f395469d38c76ec47cd1a6c5a53fbc3f13f737b96eaf7535f4e6b367e643381" +checksum = "214ee3792e504ee1ce206b36dcafa4f328ca313d1e2ac0b41433d68ef4e14260" dependencies = [ "bstr", "gix-glob", @@ -1260,32 +1233,35 @@ dependencies = [ [[package]] name = "gix-command" -version = "0.2.10" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7" +checksum = "ce1ffc7db3fb50b7dae6ecd937a3527cb725f444614df2ad8988d81806f13f09" dependencies = [ "bstr", + "gix-path", + "gix-trace", + "shell-words", ] [[package]] name = "gix-commitgraph" -version = "0.22.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a7007ba021f059803afaf6f8a48872422abc20550ac12ede6ddea2936cec36" +checksum = "82dbd7fb959862e3df2583331f0ad032ac93533e8a52f1b0694bc517f5d292bc" dependencies = [ "bstr", "gix-chunk", "gix-features", "gix-hash", - "memmap2 0.9.3", + "memmap2", "thiserror", ] [[package]] name = "gix-config" -version = "0.31.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb" +checksum = "e62bf2073b6ce3921ffa6d8326f645f30eec5fc4a8e8a4bc0fcb721a2f3f69dc" dependencies = [ "bstr", "gix-config-value", @@ -1304,9 +1280,9 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e" +checksum = "5b8a1e7bfb37a46ed0b8468db37a6d8a0a61d56bdbe4603ae492cb322e5f3958" dependencies = [ "bitflags 2.4.1", "bstr", @@ -1317,9 +1293,9 @@ dependencies = [ [[package]] name = "gix-credentials" -version = "0.21.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c5c5d74069b842a1861e581027ac6b7ad9ff66f5911c89b9f45484d7ebda6a4" +checksum = "206ede3fe433abba3c8b0174179d5bbac65ae3f0d9187e2ea96c0494db6a139f" dependencies = [ "bstr", "gix-command", @@ -1327,6 +1303,7 @@ dependencies = [ "gix-path", "gix-prompt", "gix-sec", + "gix-trace", "gix-url", "thiserror", ] @@ -1345,10 +1322,11 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.37.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931394f69fb8c9ed6afc0aae3487bd869e936339bcc13ed8884472af072e0554" +checksum = "cbdcb5e49c4b9729dd1c361040ae5c3cd7c497b2260b18c954f62db3a63e98cf" dependencies = [ + "bstr", "gix-hash", "gix-object", "thiserror", @@ -1356,12 +1334,13 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45d5cf0321178883e38705ab2b098f625d609a7d4c391b33ac952eff2c490f2" +checksum = "b4669218f3ec0cbbf8f16857b32200890f8ca585f36f5817242e4115fe4551af" dependencies = [ "bstr", "dunce", + "gix-fs", "gix-hash", "gix-path", "gix-ref", @@ -1371,15 +1350,16 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.36.1" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2" +checksum = "184f7f7d4e45db0e2a362aeaf12c06c5e84817d0ef91d08e8e90170dad9f0b07" dependencies = [ "bytes", "crc32fast", "flate2", "gix-hash", "gix-trace", + "gix-utils", "libc", "once_cell", "prodash", @@ -1390,9 +1370,9 @@ dependencies = [ [[package]] name = "gix-filter" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92f674d3fdb6b1987b04521ec9a5b7be8650671f2c4bbd17c3c81e2a364242ff" +checksum = "9240862840fb740d209422937195e129e4ed3da49af212383260134bea8f6c1a" dependencies = [ "bstr", "encoding_rs", @@ -1404,24 +1384,26 @@ dependencies = [ "gix-path", "gix-quote", "gix-trace", + "gix-utils", "smallvec", "thiserror", ] [[package]] name = "gix-fs" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107" +checksum = "4436e883d5769f9fb18677b8712b49228357815f9e4104174a6fc2d8461a437b" dependencies = [ "gix-features", + "gix-utils", ] [[package]] name = "gix-glob" -version = "0.14.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19" +checksum = "4965a1d06d0ab84a29d4a67697a97352ab14ae1da821084e5afb1fd6d8191ca0" dependencies = [ "bitflags 2.4.1", "bstr", @@ -1431,19 +1413,19 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.13.3" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0" +checksum = "b0ed89cdc1dce26685c80271c4287077901de3c3dd90234d5fa47c22b2268653" dependencies = [ - "faster-hex 0.9.0", + "faster-hex", "thiserror", ] [[package]] name = "gix-hashtable" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb61880816d7ec4f0b20606b498147d480860ddd9133ba542628df2f548d3ca" +checksum = "ebe47d8c0887f82355e2e9e16b6cecaa4d5e5346a7a474ca78ff94de1db35a5b" dependencies = [ "gix-hash", "hashbrown 0.14.3", @@ -1452,21 +1434,22 @@ dependencies = [ [[package]] name = "gix-ignore" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a215cc8cf21645bca131fcf6329d3ebd46299c47dbbe27df71bb1ca9e328b879" +checksum = "1f7069aaca4a05784c4cb44e392f0eaf627c6e57e05d3100c0e2386a37a682f0" dependencies = [ "bstr", "gix-glob", "gix-path", + "gix-trace", "unicode-bom", ] [[package]] name = "gix-index" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83a4fcc121b2f2e109088f677f89f85e7a8ebf39e8e6659c0ae54d4283b1650" +checksum = "1d7152181ba8f0a3addc5075dd612cea31fc3e252b29c8be8c45f4892bf87426" dependencies = [ "bitflags 2.4.1", "bstr", @@ -1480,16 +1463,18 @@ dependencies = [ "gix-object", "gix-traverse", "itoa", - "memmap2 0.7.1", + "libc", + "memmap2", + "rustix", "smallvec", "thiserror", ] [[package]] name = "gix-lock" -version = "11.0.1" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5c65e6a29830a435664891ced3f3c1af010f14900226019590ee0971a22f37" +checksum = "651e46174dc5e7d18b7b809d31937b6de3681b1debd78618c99162cc30fcf3e1" dependencies = [ "gix-tempfile", "gix-utils", @@ -1509,9 +1494,9 @@ dependencies = [ [[package]] name = "gix-negotiate" -version = "0.9.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5cdcf491ecc9ce39dcc227216c540355fe0024ae7c38e94557752ca5ebb67f" +checksum = "a163adb84149e522e991cbe27250a6e01de56f98cd05b174614ce3f8a4e8b140" dependencies = [ "bitflags 2.4.1", "gix-commitgraph", @@ -1525,9 +1510,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.38.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51" +checksum = "693ce9d30741506cb082ef2d8b797415b48e032cce0ab23eff894c19a7e4777b" dependencies = [ "bstr", "btoi", @@ -1544,13 +1529,14 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.54.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8630b56cb80d8fa684d383dad006a66401ee8314e12fbf0e566ddad8c115143b" +checksum = "8ba2fa9e81f2461b78b4d81a807867667326c84cdab48e0aed7b73a593aa1be4" dependencies = [ "arc-swap", "gix-date", "gix-features", + "gix-fs", "gix-hash", "gix-object", "gix-pack", @@ -1563,9 +1549,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.44.0" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1431ba2e30deff1405920693d54ab231c88d7c240dd6ccc936ee223d8f8697c3" +checksum = "8da5f3e78c96b76c4e6fe5e8e06b76221e4a0ee9a255aa935ed1fdf68988dfd8" dependencies = [ "clru", "gix-chunk", @@ -1575,7 +1561,7 @@ dependencies = [ "gix-object", "gix-path", "gix-tempfile", - "memmap2 0.7.1", + "memmap2", "parking_lot", "smallvec", "thiserror", @@ -1583,31 +1569,33 @@ dependencies = [ [[package]] name = "gix-packetline" -version = "0.16.7" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab" +checksum = "09ff45eef7747bde4986429a3e813478d50c2688b8f239e57bd3aa81065b285f" dependencies = [ "bstr", - "faster-hex 0.8.1", + "faster-hex", + "gix-trace", "thiserror", ] [[package]] name = "gix-packetline-blocking" -version = "0.16.6" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d8395f7501c84d6a1fe902035fdfd8cd86d89e2dd6be0200ec1a72fd3c92d39" +checksum = "ca8ef6dd3ea50e26f3bf572e90c034d033c804d340cd1eb386392f184a9ba2f7" dependencies = [ "bstr", - "faster-hex 0.8.1", + "faster-hex", + "gix-trace", "thiserror", ] [[package]] name = "gix-path" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dd0998ab245f33d40ca2267e58d542fe54185ebd1dc41923346cf28d179fb6" +checksum = "97e9ad649bf5e109562d6acba657ca428661ec08e77eaf3a755d8fa55485be9c" dependencies = [ "bstr", "gix-trace", @@ -1618,9 +1606,9 @@ dependencies = [ [[package]] name = "gix-pathspec" -version = "0.4.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbbb92f75a38ef043c8bb830b339b38d0698d7f3746968b5fcbade7a880494d" +checksum = "9cbd49750edb26b0a691e5246fc635fa554d344da825cd20fa9ee0da9c1b761f" dependencies = [ "bitflags 2.4.1", "bstr", @@ -1633,9 +1621,9 @@ dependencies = [ [[package]] name = "gix-prompt" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c9a913769516f5e9d937afac206fb76428e3d7238e538845842887fda584678" +checksum = "02bd89d058258e53e0fd6c57f13ee16c5673a83066a68e11f88626fc8cfda5f6" dependencies = [ "gix-command", "gix-config-value", @@ -1646,9 +1634,9 @@ dependencies = [ [[package]] name = "gix-protocol" -version = "0.41.1" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391e3feabdfa5f90dad6673ce59e3291ac28901b2ff248d86c5a7fbde0391e0e" +checksum = "84af465436787ff423a1b4d5bd0c1979200e7165ed04324fa03ba2235485eebc" dependencies = [ "bstr", "btoi", @@ -1675,9 +1663,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.38.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52" +checksum = "5818958994ad7879fa566f5441ebcc48f0926aa027b28948e6fbf6578894dc31" dependencies = [ "gix-actor", "gix-date", @@ -1688,17 +1676,18 @@ dependencies = [ "gix-object", "gix-path", "gix-tempfile", + "gix-utils", "gix-validate", - "memmap2 0.7.1", + "memmap2", "thiserror", "winnow", ] [[package]] name = "gix-refspec" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb0974cc41dbdb43a180c7f67aa481e1c1e160fcfa8f4a55291fd1126c1a6e7" +checksum = "613aa4d93034c5791d13bdc635e530f4ddab1412ddfb4a8215f76213177b61c7" dependencies = [ "bstr", "gix-hash", @@ -1710,9 +1699,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.23.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ca97ac73459a7f3766aa4a5638a6e37d56d4c7962bc1986fbaf4883d0772588" +checksum = "288f6549d7666db74dc3f169a9a333694fc28ecd2f5aa7b2c979c89eb556751a" dependencies = [ "bstr", "gix-date", @@ -1726,9 +1715,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.9.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16d8c892e4cd676d86f0265bf9d40cefd73d8d94f86b213b8b77d50e77efae0" +checksum = "5b9b4d91dfc5c14fee61a28c65113ded720403b65a0f46169c0460f731a5d03c" dependencies = [ "gix-commitgraph", "gix-date", @@ -1741,21 +1730,21 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c" +checksum = "f8d9bf462feaf05f2121cba7399dbc6c34d88a9cad58fc1e95027791d6a3c6d2" dependencies = [ "bitflags 2.4.1", "gix-path", "libc", - "windows", + "windows-sys 0.52.0", ] [[package]] name = "gix-submodule" -version = "0.5.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bba78c8d12aa24370178453ec3a472ff08dfaa657d116229f57f2c9cd469a1c2" +checksum = "73182f6c1f5ed1ed94ba16581ac62593d5e29cd1c028b2af618f836283b8f8d4" dependencies = [ "bstr", "gix-config", @@ -1768,9 +1757,9 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "11.0.1" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388dd29114a86ec69b28d1e26d6d63a662300ecf61ab3f4cc578f7d7dc9e7e23" +checksum = "2d337955b7af00fb87120d053d87cdfb422a80b9ff7a3aa4057a99c79422dc30" dependencies = [ "gix-fs", "libc", @@ -1781,15 +1770,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e1127ede0475b58f4fe9c0aaa0d9bb0bad2af90bbd93ccd307c8632b863d89" +checksum = "02b202d766a7fefc596e2cc6a89cda8ad8ad733aed82da635ac120691112a9b1" [[package]] name = "gix-transport" -version = "0.38.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f209a93364e24f20319751bc11092272e2f3fe82bb72592b2822679cf5be752" +checksum = "58aba2869cc38937bc834b068c93e09e2ab1119bac626f0464d100c1438b799a" dependencies = [ "base64 0.21.5", "bstr", @@ -1806,9 +1795,9 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.34.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d050ec7d4e1bb76abf0636cf4104fb915b70e54e3ced9a4427c999100ff38a" +checksum = "bfc30c5b5e4e838683b59e1b0574ce6bc1c35916df9709aaab32bb7751daf08b" dependencies = [ "gix-commitgraph", "gix-date", @@ -1822,9 +1811,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.25.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c427a1a11ccfa53a4a2da47d9442c2241deee63a154bc15cc14b8312fbc4005" +checksum = "26f1981ecc700f4fd73ae62b9ca2da7c8816c8fd267f0185e3f8c21e967984ac" dependencies = [ "bstr", "gix-features", @@ -1836,11 +1825,12 @@ dependencies = [ [[package]] name = "gix-utils" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6225e2de30b6e9bca2d9f1cc4731640fcef0fb3cabddceee366e7e85d3e94f" +checksum = "56e839f3d0798b296411263da6bee780a176ef8008a5dfc31287f7eda9266ab8" dependencies = [ "fastrand", + "unicode-normalization", ] [[package]] @@ -1855,9 +1845,9 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.27.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddaf79e721dba64fe726a42f297a3c8ed42e55cdc0d81ca68452f2def3c2d7fd" +checksum = "ca36bb3dc54038c66507dc75c4d8edbee2d6d5cc45227b4eb508ad13dd60a006" dependencies = [ "bstr", "gix-attributes", @@ -1873,9 +1863,9 @@ dependencies = [ [[package]] name = "gix-worktree-state" -version = "0.4.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34a2fcccdcaf3c71c00a03df31c9aa459d444cabbec4ed9ca1fa64e43406bed4" +checksum = "8ae178614b70bdb0c7f6f21b8c9fb711ab78bd7e8e1866f565fcf28876747f1d" dependencies = [ "bstr", "gix-features", @@ -1963,6 +1953,15 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -2018,17 +2017,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.11" @@ -2127,7 +2115,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.51.1", + "windows-core", ] [[package]] @@ -2145,16 +2133,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2239,18 +2217,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2 0.5.5", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -2422,12 +2388,6 @@ dependencies = [ "cc", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.12" @@ -2450,21 +2410,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -2491,15 +2436,6 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -[[package]] -name = "memmap2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.9.3" @@ -2919,15 +2855,9 @@ dependencies = [ [[package]] name = "prodash" -version = "26.2.2" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf" - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" [[package]] name = "quote" @@ -3070,7 +3000,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "trust-dns-resolver", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -3079,16 +3008,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -3453,6 +3372,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.2.0" @@ -3561,7 +3486,7 @@ dependencies = [ "ed25519-dalek 2.0.0", "ethnum", "gix", - "heck", + "heck 0.4.1", "hex", "http", "hyper", @@ -3579,7 +3504,6 @@ dependencies = [ "sep5", "serde", "serde-aux", - "serde_derive", "serde_json", "sha2 0.10.8", "shlex", @@ -3594,6 +3518,8 @@ dependencies = [ "stellar-strkey 0.0.7", "stellar-xdr", "strsim", + "strum 0.17.1", + "strum_macros 0.17.1", "tempfile", "termcolor", "termcolor_output", @@ -3604,6 +3530,7 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", + "ureq", "wasm-opt", "wasmparser 0.90.0", "which", @@ -3815,7 +3742,7 @@ name = "soroban-spec-typescript" version = "20.3.0" dependencies = [ "base64 0.21.5", - "heck", + "heck 0.4.1", "include_dir", "itertools 0.10.5", "pretty_assertions", @@ -3944,19 +3871,37 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "530efb820d53b712f4e347916c5e7ed20deb76a4f0457943b3182fb889b06d2c" + [[package]] name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum_macros" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6e163a520367c465f59e0a61a23cfae3b10b6546d78b6f672a382be79f7110" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "strum_macros" version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -4384,52 +4329,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -4469,6 +4368,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.11" @@ -4481,6 +4386,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +dependencies = [ + "base64 0.21.5", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-webpki", + "serde", + "serde_json", + "url", + "webpki-roots", +] + [[package]] name = "url" version = "2.5.0" @@ -4488,7 +4411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -4624,8 +4547,8 @@ checksum = "effbef3bd1dde18acb401f73e740a6f3d4a1bc651e9773bddc512fe4d8d68f67" dependencies = [ "anyhow", "libc", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "tempfile", "thiserror", "wasm-opt-cxx-sys", @@ -4730,12 +4653,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "widestring" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" - [[package]] name = "winapi" version = "0.3.9" @@ -4767,16 +4684,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.0", -] - [[package]] name = "windows-core" version = "0.51.1" @@ -4786,15 +4693,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index bd68ae2bb..c37ad0d7b 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -53,9 +53,8 @@ clap = { version = "4.1.8", features = [ ] } base64 = { workspace = true } thiserror = { workspace = true } -serde = "1.0.82" -serde_derive = "1.0.82" -serde_json = "1.0.82" +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } serde-aux = "4.1.2" hex = { workspace = true } num-bigint = "0.4" @@ -93,10 +92,14 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } cargo_metadata = "0.15.4" pathdiff = "0.2.1" dotenvy = "0.15.7" -gix = { version = "0.55.2", default-features = false, features = [ +strum = "0.17.1" +strum_macros = "0.17.1" +gix = { version = "0.58.0", default-features = false, features = [ "blocking-http-transport-reqwest-rust-tls", "worktree-mutation", ] } +ureq = {version = "2.9.1", features = ["json"]} + tempfile = "3.8.1" toml_edit = "0.21.0" # For hyper-tls diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 435ea20e2..5db62f81b 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -1,62 +1,23 @@ -use core::fmt; use std::fs::read_to_string; use std::path::Path; use std::{env, fs, io}; -use clap::Parser; +use clap::builder::{PossibleValue, PossibleValuesParser, ValueParser}; +use clap::{Parser, ValueEnum}; +use serde::Deserialize; use std::num::NonZeroU32; use std::sync::atomic::AtomicBool; use toml_edit::{Document, Formatted, InlineTable, TomlError, Value}; -#[derive(Clone, Debug, PartialEq, clap::ValueEnum)] -pub enum ExampleContract { - Account, - Alloc, - AtomicMultiswap, - AtomicSwap, - Auth, - CrossContract, - CustomTypes, - DeepContractAuth, - Deployer, - Errors, - Events, - Fuzzing, - Increment, - LiquidityPool, - Logging, - SimpleAccount, - SingleOffer, - Timelock, - Token, - UpgradeableContract, -} - -impl fmt::Display for ExampleContract { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ExampleContract::Account => write!(f, "account"), - ExampleContract::Alloc => write!(f, "alloc"), - ExampleContract::AtomicMultiswap => write!(f, "atomic_multiswap"), - ExampleContract::AtomicSwap => write!(f, "atomic_swap"), - ExampleContract::Auth => write!(f, "auth"), - ExampleContract::CrossContract => write!(f, "cross_contract"), - ExampleContract::CustomTypes => write!(f, "custom_types"), - ExampleContract::DeepContractAuth => write!(f, "deep_contract_auth"), - ExampleContract::Deployer => write!(f, "deployer"), - ExampleContract::Errors => write!(f, "errors"), - ExampleContract::Events => write!(f, "events"), - ExampleContract::Fuzzing => write!(f, "fuzzing"), - ExampleContract::Increment => write!(f, "increment"), - ExampleContract::LiquidityPool => write!(f, "liquidity_pool"), - ExampleContract::Logging => write!(f, "logging"), - ExampleContract::SimpleAccount => write!(f, "simple_account"), - ExampleContract::SingleOffer => write!(f, "single_offer"), - ExampleContract::Timelock => write!(f, "timelock"), - ExampleContract::Token => write!(f, "token"), - ExampleContract::UpgradeableContract => write!(f, "upgradeable_contract"), - } - } +const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; +const GITHUB_URL: &str = "https://github.com"; +const GITHUB_API_URL: &str = + "https://api.github.com/repos/stellar/soroban-examples/git/trees/main?recursive=1"; + +#[derive(Clone, Debug, ValueEnum, PartialEq)] +pub enum FrontendTemplate { + Astro, + None, } #[derive(Parser, Debug, Clone)] @@ -64,9 +25,68 @@ impl fmt::Display for ExampleContract { pub struct Cmd { pub project_path: String, - /// An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default. - #[arg(short, long, num_args = 1..=20)] - pub with_example: Vec, + #[arg(short, long, num_args = 1.., value_parser=possible_example_values(), long_help=with_example_help())] + pub with_example: Vec, + + #[arg( + short, + long, + default_value = "", + long_help = "An optional flag to pass in a url for a frontend template repository." + )] + pub frontend_template: String, +} + +fn possible_example_values() -> ValueParser { + // If fetching the example contracts from the soroban-examples repo succeeds, return a parser with the example contracts. + if let Ok(examples) = get_valid_examples() { + let parser = PossibleValuesParser::new(examples.iter().map(PossibleValue::new)); + return parser.into(); + } + + // If fetching with example contracts fails, return a string parser that will allow for any value. It will be ignored in `init`. + ValueParser::string() +} + +fn with_example_help() -> String { + if check_internet_connection() { + "An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default.".to_owned() + } else { + "⚠️ Failed to fetch additional example contracts from soroban-examples repo. You can continue with initializing - the default hello_world contract will still be included".to_owned() + } +} + +#[derive(Deserialize, Debug)] +struct RepoPath { + path: String, + #[serde(rename = "type")] + type_field: String, +} + +#[derive(Deserialize, Debug)] +struct ReqBody { + tree: Vec, +} + +fn get_valid_examples() -> Result, Error> { + let body: ReqBody = ureq::get(GITHUB_API_URL) + .call() + .map_err(Box::new)? + .into_json()?; + let mut valid_examples = Vec::new(); + for item in body.tree { + if item.type_field == "blob" + || item.path.starts_with('.') + || item.path.contains('/') + || item.path == "hello_world" + { + continue; + } + + valid_examples.push(item.path); + } + + Ok(valid_examples) } #[derive(thiserror::Error, Debug)] @@ -87,9 +107,13 @@ pub enum Error { #[error("Failed to parse Cargo.toml: {0}")] TomlParseError(#[from] TomlError), -} -const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; + #[error("Failed to fetch example contracts")] + ExampleContractFetchError(#[from] Box), + + #[error("Failed to parse package.json file: {0}")] + JsonParseError(#[from] serde_json::Error), +} impl Cmd { #[allow(clippy::unused_self)] @@ -97,28 +121,49 @@ impl Cmd { println!("ℹ️ Initializing project at {}", self.project_path); let project_path = Path::new(&self.project_path); - init(project_path, &self.with_example)?; + init(project_path, &self.frontend_template, &self.with_example)?; Ok(()) } } -fn init(project_path: &Path, with_examples: &[ExampleContract]) -> Result<(), Error> { +fn init( + project_path: &Path, + frontend_template: &String, + with_examples: &[String], +) -> Result<(), Error> { let cli_cmd_root = env!("CARGO_MANIFEST_DIR"); let template_dir_path = Path::new(cli_cmd_root) .join("src") .join("utils") .join("contract-init-template"); + // create a project dir, and copy the contents of the base template (contract-init-template) into it std::fs::create_dir_all(project_path)?; copy_contents(template_dir_path.as_path(), project_path)?; - // if there are with-contract flags, include the example contracts + if !check_internet_connection() { + println!("⚠️ It doesn't look like you're connected to the internet. We're still able to initialize a new project, but additional examples and the frontend template will not be included."); + return Ok(()); + } + + if !frontend_template.is_empty() { + // create a temp dir for the template repo + let fe_template_dir = tempfile::tempdir()?; + + // clone the template repo into the temp dir + clone_repo(frontend_template, fe_template_dir.path())?; + + // copy the frontend template files into the project + copy_frontend_files(fe_template_dir.path(), project_path); + } + + // if there are --with-example flags, include the example contracts if include_example_contracts(with_examples) { - // create an examples temp dir in the temp dir + // create an examples temp dir let examples_dir = tempfile::tempdir()?; - // clone the soroban-examples repo into temp dir + // clone the soroban-examples repo into the temp dir clone_repo(SOROBAN_EXAMPLES_URL, examples_dir.path())?; // copy the example contracts into the project @@ -152,6 +197,12 @@ fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { copy_contents(&path, &new_path)?; } else { if file_exists(&new_path.to_string_lossy()) { + //if file is .gitignore, overwrite the file with a new .gitignore file + if path.to_string_lossy().contains(".gitignore") { + std::fs::copy(&path, &new_path)?; + continue; + } + println!( "ℹ️ Skipped creating {} as it already exists", &new_path.to_string_lossy() @@ -175,12 +226,12 @@ fn file_exists(file_path: &str) -> bool { } } -fn include_example_contracts(contracts: &[ExampleContract]) -> bool { +fn include_example_contracts(contracts: &[String]) -> bool { !contracts.is_empty() } fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { - let mut fetch = gix::clone::PrepareFetch::new( + let mut prepare = gix::clone::PrepareFetch::new( from_url, to_path, gix::create::Kind::WithWorktree, @@ -195,21 +246,17 @@ fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { NonZeroU32::new(1).unwrap(), )); - let (mut prepare, _outcome) = fetch + let (mut checkout, _outcome) = prepare .fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false)) .map_err(Box::new)?; let (_repo, _outcome) = - prepare.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?; + checkout.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?; Ok(()) } -fn copy_example_contracts( - from: &Path, - to: &Path, - contracts: &[ExampleContract], -) -> Result<(), Error> { +fn copy_example_contracts(from: &Path, to: &Path, contracts: &[String]) -> Result<(), Error> { let project_contracts_path = to.join("contracts"); for contract in contracts { println!("ℹ️ Initializing example contract: {contract}"); @@ -246,43 +293,66 @@ fn edit_contract_cargo_file(contract_path: &Path) -> Result<(), Error> { Ok(()) } +fn copy_frontend_files(from: &Path, to: &Path) { + println!("ℹ️ Initializing with frontend template"); + let _ = copy_contents(from, to); + let _ = edit_package_json_files(to); +} + +fn edit_package_json_files(project_path: &Path) -> Result<(), Error> { + let package_name = project_path.file_name().unwrap(); + edit_package_name(project_path, package_name, "package.json")?; + edit_package_name(project_path, package_name, "package-lock.json") +} + +fn edit_package_name( + project_path: &Path, + package_name: &std::ffi::OsStr, + file_name: &str, +) -> Result<(), Error> { + let file_path = project_path.join(file_name); + let file_contents = read_to_string(&file_path)?; + + let mut doc: serde_json::Value = serde_json::from_str(&file_contents)?; + + doc["name"] = serde_json::json!(package_name.to_string_lossy()); + + std::fs::write(&file_path, doc.to_string())?; + + Ok(()) +} + +fn check_internet_connection() -> bool { + if let Ok(_req) = ureq::get(GITHUB_URL).call() { + return true; + } + + false +} + #[cfg(test)] mod tests { use std::fs::read_to_string; use super::*; + const TEST_PROJECT_NAME: &str = "test-project"; + #[test] fn test_init() { let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join("project"); + let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); let with_examples = vec![]; - init(project_dir.as_path(), &with_examples).unwrap(); - - assert!(project_dir.as_path().join("README.md").exists()); - assert!(project_dir.as_path().join("contracts").exists()); - assert!(project_dir.as_path().join("Cargo.toml").exists()); - - // check that it includes the default hello-world contract - assert!(project_dir - .as_path() - .join("contracts") - .join("hello_world") - .exists()); + init(project_dir.as_path(), &String::new(), &with_examples).unwrap(); + + assert_base_template_files_exist(&project_dir); + assert_default_hello_world_contract_files_exist(&project_dir); + assert_base_excluded_paths_do_not_exist(&project_dir); + // check that the contract's Cargo.toml file uses the workspace for dependencies - let contract_cargo_path = project_dir - .as_path() - .join("contracts") - .join("hello_world") - .join("Cargo.toml"); - let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); - assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); + assert_contract_cargo_file_uses_workspace(&project_dir, "hello_world"); - // check that it does not include certain template files and directories - assert!(!project_dir.as_path().join(".git").exists()); - assert!(!project_dir.as_path().join(".github").exists()); - assert!(!project_dir.as_path().join("Cargo.lock").exists()); - assert!(!project_dir.as_path().join(".vscode").exists()); + assert_base_excluded_paths_do_not_exist(&project_dir); temp_dir.close().unwrap(); } @@ -290,45 +360,22 @@ mod tests { #[test] fn test_init_including_example_contract() { let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join("project"); - let with_examples = vec![ExampleContract::Alloc]; - init(project_dir.as_path(), &with_examples).unwrap(); - - assert!(project_dir.as_path().join("README.md").exists()); - assert!(project_dir - .as_path() - .join("contracts") - .join("alloc") - .exists()); - - // check that it does not include certain template files and directories - assert!(!project_dir.as_path().join(".git").exists()); - assert!(!project_dir.as_path().join(".github").exists()); - assert!(!project_dir.as_path().join("Cargo.lock").exists()); - assert!(!project_dir.as_path().join(".vscode").exists()); - - // check that it does not include certain contract files - assert!(!project_dir - .as_path() - .join("contracts") - .join("alloc") - .join("Makefile") - .exists()); - assert!(!project_dir - .as_path() - .join("contracts") - .join("alloc") - .join("Cargo.lock") - .exists()); + let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); + let with_examples = ["alloc".to_owned()]; + init(project_dir.as_path(), &String::new(), &with_examples).unwrap(); - // check that the contract's Cargo.toml file uses the workspace for dependencies - let contract_cargo_path = project_dir - .as_path() - .join("contracts") - .join("alloc") - .join("Cargo.toml"); - let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); - assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); + assert_base_template_files_exist(&project_dir); + assert_default_hello_world_contract_files_exist(&project_dir); + assert_base_excluded_paths_do_not_exist(&project_dir); + + // check that alloc contract files exist + assert_contract_files_exist(&project_dir, "alloc"); + + // check that expected files are excluded from the alloc contract dir + assert_example_contract_excluded_files_do_not_exist(&project_dir, "alloc"); + + // check that the alloc contract's Cargo.toml file uses the workspace for dependencies + assert_contract_cargo_file_uses_workspace(&project_dir, "alloc"); temp_dir.close().unwrap(); } @@ -337,20 +384,139 @@ mod tests { fn test_init_including_multiple_example_contracts() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); - let with_examples = vec![ExampleContract::Account, ExampleContract::AtomicSwap]; - init(project_dir.as_path(), &with_examples).unwrap(); - - assert!(project_dir - .as_path() - .join("contracts") - .join("account") - .exists()); - assert!(project_dir - .as_path() - .join("contracts") - .join("atomic_swap") - .exists()); + let with_examples = ["account".to_owned(), "atomic_swap".to_owned()]; + init(project_dir.as_path(), &String::new(), &with_examples).unwrap(); + + assert_base_template_files_exist(&project_dir); + assert_default_hello_world_contract_files_exist(&project_dir); + assert_base_excluded_paths_do_not_exist(&project_dir); + + // check that account contract files exist and that expected files are excluded + assert_contract_files_exist(&project_dir, "account"); + assert_example_contract_excluded_files_do_not_exist(&project_dir, "account"); + assert_contract_cargo_file_uses_workspace(&project_dir, "account"); + + // check that atomic_swap contract files exist and that expected files are excluded + assert_contract_files_exist(&project_dir, "atomic_swap"); + assert_example_contract_excluded_files_do_not_exist(&project_dir, "atomic_swap"); + assert_contract_cargo_file_uses_workspace(&project_dir, "atomic_swap"); + + temp_dir.close().unwrap(); + } + + #[test] + fn test_init_with_invalid_example_contract() { + let temp_dir = tempfile::tempdir().unwrap(); + let project_dir = temp_dir.path().join("project"); + let with_examples = ["invalid_example".to_owned(), "atomic_swap".to_owned()]; + assert!(init(project_dir.as_path(), &String::new(), &with_examples,).is_err()); + + temp_dir.close().unwrap(); + } + + #[test] + fn test_init_with_frontend_template() { + let temp_dir = tempfile::tempdir().unwrap(); + let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); + let with_examples = vec![]; + init( + project_dir.as_path(), + &"https://github.com/AhaLabs/soroban-astro-template".to_string(), + &with_examples, + ) + .unwrap(); + + assert_base_template_files_exist(&project_dir); + assert_default_hello_world_contract_files_exist(&project_dir); + assert_base_excluded_paths_do_not_exist(&project_dir); + + // check that the contract's Cargo.toml file uses the workspace for dependencies + assert_contract_cargo_file_uses_workspace(&project_dir, "hello_world"); + assert_base_excluded_paths_do_not_exist(&project_dir); + + assert_astro_files_exist(&project_dir); + assert_gitignore_includes_astro_paths(&project_dir); + assert_package_json_files_have_correct_name(&project_dir); temp_dir.close().unwrap(); } + + // test helpers + fn assert_base_template_files_exist(project_dir: &Path) { + let expected_paths = ["contracts", "Cargo.toml", "README.md"]; + for path in &expected_paths { + assert!(project_dir.join(path).exists()); + } + } + + fn assert_default_hello_world_contract_files_exist(project_dir: &Path) { + assert_contract_files_exist(project_dir, "hello_world"); + } + + fn assert_contract_files_exist(project_dir: &Path, contract_name: &str) { + let contract_dir = project_dir.join("contracts").join(contract_name); + + assert!(contract_dir.exists()); + assert!(contract_dir.as_path().join("Cargo.toml").exists()); + assert!(contract_dir.as_path().join("src").join("lib.rs").exists()); + assert!(contract_dir.as_path().join("src").join("test.rs").exists()); + } + + fn assert_contract_cargo_file_uses_workspace(project_dir: &Path, contract_name: &str) { + let contract_dir = project_dir.join("contracts").join(contract_name); + let cargo_toml_path = contract_dir.as_path().join("Cargo.toml"); + let cargo_toml_str = read_to_string(cargo_toml_path).unwrap(); + assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); + } + + fn assert_example_contract_excluded_files_do_not_exist( + project_dir: &Path, + contract_name: &str, + ) { + let contract_dir = project_dir.join("contracts").join(contract_name); + assert!(!contract_dir.as_path().join("Makefile").exists()); + assert!(!contract_dir.as_path().join("Cargo.lock").exists()); + } + + fn assert_base_excluded_paths_do_not_exist(project_dir: &Path) { + let excluded_paths = [ + ".git", + ".github", + "Makefile", + "Cargo.lock", + ".vscode", + "target", + ]; + for path in &excluded_paths { + assert!(!project_dir.join(path).exists()); + } + } + + fn assert_gitignore_includes_astro_paths(project_dir: &Path) { + let gitignore_path = project_dir.join(".gitignore"); + let gitignore_str = read_to_string(gitignore_path).unwrap(); + assert!(gitignore_str.contains(".astro/")); + assert!(gitignore_str.contains("node_modules")); + assert!(gitignore_str.contains("npm-debug.log*")); + } + + fn assert_astro_files_exist(project_dir: &Path) { + assert!(project_dir.join("public").exists()); + assert!(project_dir.join("src").exists()); + assert!(project_dir.join("src").join("components").exists()); + assert!(project_dir.join("src").join("layouts").exists()); + assert!(project_dir.join("src").join("pages").exists()); + assert!(project_dir.join("astro.config.mjs").exists()); + assert!(project_dir.join("tsconfig.json").exists()); + } + + fn assert_package_json_files_have_correct_name(project_dir: &Path) { + let package_json_path = project_dir.join("package.json"); + let package_json_str = read_to_string(package_json_path).unwrap(); + assert!(package_json_str.contains(&format!("\"name\":\"{TEST_PROJECT_NAME}\""))); + + let package_lock_json_path = project_dir.join("package-lock.json"); + let package_lock_json_str = read_to_string(package_lock_json_path).unwrap(); + assert!(package_lock_json_str.contains(&format!("\"name\":\"{TEST_PROJECT_NAME}\""))); + } } diff --git a/cmd/soroban-cli/src/utils/contract-init-template/README.md b/cmd/soroban-cli/src/utils/contract-init-template/README.md index d78ed28e5..012e23c44 100644 --- a/cmd/soroban-cli/src/utils/contract-init-template/README.md +++ b/cmd/soroban-cli/src/utils/contract-init-template/README.md @@ -1,3 +1,21 @@ -# Soroban Init Template +# Soroban Project -This repository can be used as a template for creating a Soroban project. You can either clone this repository directly, or soon will be able to use the `soroban init` command from the soroban CLI. This first iteration just creates a bare-bones Rust project setup as a Rust workspace, but in the future it will also include options for creating an frontend that is able to interact with contracts in the `contracts` directory as well. +## Project Structure + +This repository uses the recommended structure for a Soroban project: +```text +. +├── contracts +│   └── hello_world +│   ├── src +│   │   ├── lib.rs +│   │   └── test.rs +│   └── Cargo.toml +├── Cargo.toml +└── README.md +``` + +- New Soroban contracts can be put in `contracts`, each in their own directory. There is already a `hello_world` contract in there to get you started. +- If you initialized this project with any other example contracts via `--with-example`, those contracts will be in the `contracts` directory as well. +- Contracts should have their own `Cargo.toml` files that rely on the top-level `Cargo.toml` workspace for their dependencies. +- Frontend libraries can be added to the top-level directory as well. If you initialized this project with a frontend template via `--frontend-template` you will have those files already included. \ No newline at end of file diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 34ab0caa2..9e9587d29 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -680,10 +680,13 @@ Initialize a Soroban project with an example contract ###### **Options:** -* `-w`, `--with-example ` — An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default +* `-w`, `--with-example ` - Possible values: `account`, `alloc`, `atomic-multiswap`, `atomic-swap`, `auth`, `cross-contract`, `custom-types`, `deep-contract-auth`, `deployer`, `errors`, `events`, `fuzzing`, `increment`, `liquidity-pool`, `logging`, `simple-account`, `single-offer`, `timelock`, `token`, `upgradeable-contract` + Possible values: `account`, `alloc`, `atomic_multiswap`, `atomic_swap`, `auth`, `cross_contract`, `custom_types`, `deep_contract_auth`, `deployer`, `errors`, `events`, `fuzzing`, `increment`, `liquidity_pool`, `logging`, `mint-lock`, `simple_account`, `single_offer`, `timelock`, `token`, `upgradeable_contract`, `workspace` +* `-f`, `--frontend-template ` + + Default value: ``