| ... | ... | @@ -41,6 +41,21 @@ pub fn isSep(byte: u8) bool { |
| 41 | 41 | }; |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | pub const PathType = enum { |
| 45 | windows, |
| 46 | uefi, |
| 47 | posix, |
| 48 | |
| 49 | /// Returns true if `c` is a valid path separator for the `path_type`. |
| 50 | pub inline fn isSep(comptime path_type: PathType, comptime T: type, c: T) bool { |
| 51 | return switch (path_type) { |
| 52 | .windows => c == '/' or c == '\\', |
| 53 | .posix => c == '/', |
| 54 | .uefi => c == '\\', |
| 55 | }; |
| 56 | } |
| 57 | }; |
| 58 | |
| 44 | 59 | /// This is different from mem.join in that the separator will not be repeated if |
| 45 | 60 | /// it is found at the end or beginning of a pair of consecutive paths. |
| 46 | 61 | fn joinSepMaybeZ(allocator: Allocator, separator: u8, comptime sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 { |
| ... | ... | @@ -1318,3 +1333,581 @@ test "stem" { |
| 1318 | 1333 | try testStem(" ", " "); |
| 1319 | 1334 | try testStem("", ""); |
| 1320 | 1335 | } |
| 1336 | |
| 1337 | /// A path component iterator that can move forwards and backwards. |
| 1338 | /// The 'root' of the path (`/` for POSIX, things like `C:\`, `\\server\share\`, etc |
| 1339 | /// for Windows) is treated specially and will never be returned by any of the |
| 1340 | /// `first`, `last`, `next`, or `previous` functions. |
| 1341 | /// Multiple consecutive path separators are skipped (treated as a single separator) |
| 1342 | /// when iterating. |
| 1343 | /// All returned component names/paths are slices of the original path. |
| 1344 | /// There is no normalization of paths performed while iterating. |
| 1345 | pub fn ComponentIterator(comptime path_type: PathType, comptime T: type) type { |
| 1346 | return struct { |
| 1347 | path: []const T, |
| 1348 | root_end_index: usize = 0, |
| 1349 | start_index: usize = 0, |
| 1350 | end_index: usize = 0, |
| 1351 | |
| 1352 | const Self = @This(); |
| 1353 | |
| 1354 | pub const Component = struct { |
| 1355 | /// The current component's path name, e.g. 'b'. |
| 1356 | /// This will never contain path separators. |
| 1357 | name: []const T, |
| 1358 | /// The full path up to and including the current component, e.g. '/a/b' |
| 1359 | /// This will never contain trailing path separators. |
| 1360 | path: []const T, |
| 1361 | }; |
| 1362 | |
| 1363 | const InitError = switch (path_type) { |
| 1364 | .windows => error{BadPathName}, |
| 1365 | else => error{}, |
| 1366 | }; |
| 1367 | |
| 1368 | /// After `init`, `next` will return the first component after the root |
| 1369 | /// (there is no need to call `first` after `init`). |
| 1370 | /// To iterate backwards (from the end of the path to the beginning), call `last` |
| 1371 | /// after `init` and then iterate via `previous` calls. |
| 1372 | /// For Windows paths, `error.BadPathName` is returned if the `path` has an explicit |
| 1373 | /// namespace prefix (`\\.\`, `\\?\`, or `\??\`) or if it is a UNC path with more |
| 1374 | /// than two path separators at the beginning. |
| 1375 | pub fn init(path: []const T) InitError!Self { |
| 1376 | const root_end_index: usize = switch (path_type) { |
| 1377 | .posix, .uefi => posix: { |
| 1378 | // Root on UEFI and POSIX only differs by the path separator |
| 1379 | var root_end_index: usize = 0; |
| 1380 | while (true) : (root_end_index += 1) { |
| 1381 | if (root_end_index >= path.len or !path_type.isSep(T, path[root_end_index])) { |
| 1382 | break; |
| 1383 | } |
| 1384 | } |
| 1385 | break :posix root_end_index; |
| 1386 | }, |
| 1387 | .windows => windows: { |
| 1388 | // Namespaces other than the Win32 file namespace are tricky |
| 1389 | // and basically impossible to determine a 'root' for, since it's |
| 1390 | // possible to construct an effectively arbitrarily long 'root', |
| 1391 | // e.g. `\\.\GLOBALROOT\??\UNC\localhost\C$\foo` is a |
| 1392 | // possible path that would be effectively equivalent to |
| 1393 | // `C:\foo`, and the `GLOBALROOT\??\` part can also be recursive, |
| 1394 | // so `GLOBALROOT\??\GLOBALROOT\??\...` would work for any number |
| 1395 | // of repetitions. Therefore, paths with an explicit namespace prefix |
| 1396 | // (\\.\, \??\, \\?\) are not allowed here. |
| 1397 | if (std.os.windows.getNamespacePrefix(T, path) != .none) { |
| 1398 | return error.BadPathName; |
| 1399 | } |
| 1400 | const windows_path_type = std.os.windows.getUnprefixedPathType(T, path); |
| 1401 | break :windows switch (windows_path_type) { |
| 1402 | .relative => 0, |
| 1403 | .root_local_device => path.len, |
| 1404 | .rooted => 1, |
| 1405 | .unc_absolute => unc: { |
| 1406 | var end_index: usize = 2; |
| 1407 | // Any extra separators between the first two and the server name are not allowed |
| 1408 | // and will always lead to STATUS_OBJECT_PATH_INVALID if it is attempted |
| 1409 | // to be used. |
| 1410 | if (end_index < path.len and path_type.isSep(T, path[end_index])) { |
| 1411 | return error.BadPathName; |
| 1412 | } |
| 1413 | // Server |
| 1414 | while (end_index < path.len and !path_type.isSep(T, path[end_index])) { |
| 1415 | end_index += 1; |
| 1416 | } |
| 1417 | // Slash(es) after server |
| 1418 | while (end_index < path.len and path_type.isSep(T, path[end_index])) { |
| 1419 | end_index += 1; |
| 1420 | } |
| 1421 | // Share |
| 1422 | while (end_index < path.len and !path_type.isSep(T, path[end_index])) { |
| 1423 | end_index += 1; |
| 1424 | } |
| 1425 | // Slash(es) after share |
| 1426 | while (end_index < path.len and path_type.isSep(T, path[end_index])) { |
| 1427 | end_index += 1; |
| 1428 | } |
| 1429 | break :unc end_index; |
| 1430 | }, |
| 1431 | .drive_absolute => drive: { |
| 1432 | var end_index: usize = 3; |
| 1433 | while (end_index < path.len and path_type.isSep(T, path[end_index])) { |
| 1434 | end_index += 1; |
| 1435 | } |
| 1436 | break :drive end_index; |
| 1437 | }, |
| 1438 | .drive_relative => 2, |
| 1439 | }; |
| 1440 | }, |
| 1441 | }; |
| 1442 | return .{ |
| 1443 | .path = path, |
| 1444 | .root_end_index = root_end_index, |
| 1445 | .start_index = root_end_index, |
| 1446 | .end_index = root_end_index, |
| 1447 | }; |
| 1448 | } |
| 1449 | |
| 1450 | /// Returns the root of the path if it is an absolute path, or null otherwise. |
| 1451 | /// For POSIX paths, this will be `/`. |
| 1452 | /// For Windows paths, this will be something like `C:\`, `\\server\share\`, etc. |
| 1453 | /// For UEFI paths, this will be `\`. |
| 1454 | pub fn root(self: Self) ?[]const T { |
| 1455 | if (self.root_end_index == 0) return null; |
| 1456 | return self.path[0..self.root_end_index]; |
| 1457 | } |
| 1458 | |
| 1459 | /// Returns the first component (from the beginning of the path). |
| 1460 | /// For example, if the path is `/a/b/c` then this will return the `a` component. |
| 1461 | /// After calling `first`, `previous` will always return `null`, and `next` will return |
| 1462 | /// the component to the right of the one returned by `first`, if any exist. |
| 1463 | pub fn first(self: *Self) ?Component { |
| 1464 | self.start_index = self.root_end_index; |
| 1465 | self.end_index = self.start_index; |
| 1466 | while (self.end_index < self.path.len and !path_type.isSep(T, self.path[self.end_index])) { |
| 1467 | self.end_index += 1; |
| 1468 | } |
| 1469 | if (self.end_index == self.start_index) return null; |
| 1470 | return .{ |
| 1471 | .name = self.path[self.start_index..self.end_index], |
| 1472 | .path = self.path[0..self.end_index], |
| 1473 | }; |
| 1474 | } |
| 1475 | |
| 1476 | /// Returns the last component (from the end of the path). |
| 1477 | /// For example, if the path is `/a/b/c` then this will return the `c` component. |
| 1478 | /// After calling `last`, `next` will always return `null`, and `previous` will return |
| 1479 | /// the component to the left of the one returned by `last`, if any exist. |
| 1480 | pub fn last(self: *Self) ?Component { |
| 1481 | self.end_index = self.path.len; |
| 1482 | while (true) { |
| 1483 | if (self.end_index == self.root_end_index) { |
| 1484 | self.start_index = self.end_index; |
| 1485 | return null; |
| 1486 | } |
| 1487 | if (!path_type.isSep(T, self.path[self.end_index - 1])) break; |
| 1488 | self.end_index -= 1; |
| 1489 | } |
| 1490 | self.start_index = self.end_index; |
| 1491 | while (true) { |
| 1492 | if (self.start_index == self.root_end_index) break; |
| 1493 | if (path_type.isSep(T, self.path[self.start_index - 1])) break; |
| 1494 | self.start_index -= 1; |
| 1495 | } |
| 1496 | if (self.start_index == self.end_index) return null; |
| 1497 | return .{ |
| 1498 | .name = self.path[self.start_index..self.end_index], |
| 1499 | .path = self.path[0..self.end_index], |
| 1500 | }; |
| 1501 | } |
| 1502 | |
| 1503 | /// Returns the next component (the component to the right of the most recently |
| 1504 | /// returned component), or null if no such component exists. |
| 1505 | /// For example, if the path is `/a/b/c` and the most recently returned component |
| 1506 | /// is `b`, then this will return the `c` component. |
| 1507 | pub fn next(self: *Self) ?Component { |
| 1508 | var start_index = self.end_index; |
| 1509 | while (start_index < self.path.len and path_type.isSep(T, self.path[start_index])) { |
| 1510 | start_index += 1; |
| 1511 | } |
| 1512 | var end_index = start_index; |
| 1513 | while (end_index < self.path.len and !path_type.isSep(T, self.path[end_index])) { |
| 1514 | end_index += 1; |
| 1515 | } |
| 1516 | if (start_index == end_index) return null; |
| 1517 | self.start_index = start_index; |
| 1518 | self.end_index = end_index; |
| 1519 | return .{ |
| 1520 | .name = self.path[self.start_index..self.end_index], |
| 1521 | .path = self.path[0..self.end_index], |
| 1522 | }; |
| 1523 | } |
| 1524 | |
| 1525 | /// Returns the previous component (the component to the left of the most recently |
| 1526 | /// returned component), or null if no such component exists. |
| 1527 | /// For example, if the path is `/a/b/c` and the most recently returned component |
| 1528 | /// is `b`, then this will return the `a` component. |
| 1529 | pub fn previous(self: *Self) ?Component { |
| 1530 | var end_index = self.start_index; |
| 1531 | while (true) { |
| 1532 | if (end_index == self.root_end_index) return null; |
| 1533 | if (!path_type.isSep(T, self.path[end_index - 1])) break; |
| 1534 | end_index -= 1; |
| 1535 | } |
| 1536 | var start_index = end_index; |
| 1537 | while (true) { |
| 1538 | if (start_index == self.root_end_index) break; |
| 1539 | if (path_type.isSep(T, self.path[start_index - 1])) break; |
| 1540 | start_index -= 1; |
| 1541 | } |
| 1542 | if (start_index == end_index) return null; |
| 1543 | self.start_index = start_index; |
| 1544 | self.end_index = end_index; |
| 1545 | return .{ |
| 1546 | .name = self.path[self.start_index..self.end_index], |
| 1547 | .path = self.path[0..self.end_index], |
| 1548 | }; |
| 1549 | } |
| 1550 | }; |
| 1551 | } |
| 1552 | |
| 1553 | pub const NativeUtf8ComponentIterator = ComponentIterator(switch (native_os) { |
| 1554 | .windows => .windows, |
| 1555 | .uefi => .uefi, |
| 1556 | else => .posix, |
| 1557 | }, u8); |
| 1558 | |
| 1559 | pub fn componentIterator(path: []const u8) !NativeUtf8ComponentIterator { |
| 1560 | return NativeUtf8ComponentIterator.init(path); |
| 1561 | } |
| 1562 | |
| 1563 | test "ComponentIterator posix" { |
| 1564 | const PosixComponentIterator = ComponentIterator(.posix, u8); |
| 1565 | { |
| 1566 | const path = "a/b/c/"; |
| 1567 | var it = try PosixComponentIterator.init(path); |
| 1568 | try std.testing.expectEqual(@as(usize, 0), it.root_end_index); |
| 1569 | try std.testing.expect(null == it.root()); |
| 1570 | { |
| 1571 | try std.testing.expect(null == it.previous()); |
| 1572 | |
| 1573 | const first_via_next = it.next().?; |
| 1574 | try std.testing.expectEqualStrings("a", first_via_next.name); |
| 1575 | try std.testing.expectEqualStrings("a", first_via_next.path); |
| 1576 | |
| 1577 | const first = it.first().?; |
| 1578 | try std.testing.expectEqualStrings("a", first.name); |
| 1579 | try std.testing.expectEqualStrings("a", first.path); |
| 1580 | |
| 1581 | try std.testing.expect(null == it.previous()); |
| 1582 | |
| 1583 | const second = it.next().?; |
| 1584 | try std.testing.expectEqualStrings("b", second.name); |
| 1585 | try std.testing.expectEqualStrings("a/b", second.path); |
| 1586 | |
| 1587 | const third = it.next().?; |
| 1588 | try std.testing.expectEqualStrings("c", third.name); |
| 1589 | try std.testing.expectEqualStrings("a/b/c", third.path); |
| 1590 | |
| 1591 | try std.testing.expect(null == it.next()); |
| 1592 | } |
| 1593 | { |
| 1594 | const last = it.last().?; |
| 1595 | try std.testing.expectEqualStrings("c", last.name); |
| 1596 | try std.testing.expectEqualStrings("a/b/c", last.path); |
| 1597 | |
| 1598 | try std.testing.expect(null == it.next()); |
| 1599 | |
| 1600 | const second_to_last = it.previous().?; |
| 1601 | try std.testing.expectEqualStrings("b", second_to_last.name); |
| 1602 | try std.testing.expectEqualStrings("a/b", second_to_last.path); |
| 1603 | |
| 1604 | const third_to_last = it.previous().?; |
| 1605 | try std.testing.expectEqualStrings("a", third_to_last.name); |
| 1606 | try std.testing.expectEqualStrings("a", third_to_last.path); |
| 1607 | |
| 1608 | try std.testing.expect(null == it.previous()); |
| 1609 | } |
| 1610 | } |
| 1611 | |
| 1612 | { |
| 1613 | const path = "/a/b/c/"; |
| 1614 | var it = try PosixComponentIterator.init(path); |
| 1615 | try std.testing.expectEqual(@as(usize, 1), it.root_end_index); |
| 1616 | try std.testing.expectEqualStrings("/", it.root().?); |
| 1617 | { |
| 1618 | try std.testing.expect(null == it.previous()); |
| 1619 | |
| 1620 | const first_via_next = it.next().?; |
| 1621 | try std.testing.expectEqualStrings("a", first_via_next.name); |
| 1622 | try std.testing.expectEqualStrings("/a", first_via_next.path); |
| 1623 | |
| 1624 | const first = it.first().?; |
| 1625 | try std.testing.expectEqualStrings("a", first.name); |
| 1626 | try std.testing.expectEqualStrings("/a", first.path); |
| 1627 | |
| 1628 | try std.testing.expect(null == it.previous()); |
| 1629 | |
| 1630 | const second = it.next().?; |
| 1631 | try std.testing.expectEqualStrings("b", second.name); |
| 1632 | try std.testing.expectEqualStrings("/a/b", second.path); |
| 1633 | |
| 1634 | const third = it.next().?; |
| 1635 | try std.testing.expectEqualStrings("c", third.name); |
| 1636 | try std.testing.expectEqualStrings("/a/b/c", third.path); |
| 1637 | |
| 1638 | try std.testing.expect(null == it.next()); |
| 1639 | } |
| 1640 | { |
| 1641 | const last = it.last().?; |
| 1642 | try std.testing.expectEqualStrings("c", last.name); |
| 1643 | try std.testing.expectEqualStrings("/a/b/c", last.path); |
| 1644 | |
| 1645 | try std.testing.expect(null == it.next()); |
| 1646 | |
| 1647 | const second_to_last = it.previous().?; |
| 1648 | try std.testing.expectEqualStrings("b", second_to_last.name); |
| 1649 | try std.testing.expectEqualStrings("/a/b", second_to_last.path); |
| 1650 | |
| 1651 | const third_to_last = it.previous().?; |
| 1652 | try std.testing.expectEqualStrings("a", third_to_last.name); |
| 1653 | try std.testing.expectEqualStrings("/a", third_to_last.path); |
| 1654 | |
| 1655 | try std.testing.expect(null == it.previous()); |
| 1656 | } |
| 1657 | } |
| 1658 | |
| 1659 | { |
| 1660 | const path = "/"; |
| 1661 | var it = try PosixComponentIterator.init(path); |
| 1662 | try std.testing.expectEqual(@as(usize, 1), it.root_end_index); |
| 1663 | try std.testing.expectEqualStrings("/", it.root().?); |
| 1664 | |
| 1665 | try std.testing.expect(null == it.first()); |
| 1666 | try std.testing.expect(null == it.previous()); |
| 1667 | try std.testing.expect(null == it.first()); |
| 1668 | try std.testing.expect(null == it.next()); |
| 1669 | |
| 1670 | try std.testing.expect(null == it.last()); |
| 1671 | try std.testing.expect(null == it.previous()); |
| 1672 | try std.testing.expect(null == it.last()); |
| 1673 | try std.testing.expect(null == it.next()); |
| 1674 | } |
| 1675 | |
| 1676 | { |
| 1677 | const path = ""; |
| 1678 | var it = try PosixComponentIterator.init(path); |
| 1679 | try std.testing.expectEqual(@as(usize, 0), it.root_end_index); |
| 1680 | try std.testing.expect(null == it.root()); |
| 1681 | |
| 1682 | try std.testing.expect(null == it.first()); |
| 1683 | try std.testing.expect(null == it.previous()); |
| 1684 | try std.testing.expect(null == it.first()); |
| 1685 | try std.testing.expect(null == it.next()); |
| 1686 | |
| 1687 | try std.testing.expect(null == it.last()); |
| 1688 | try std.testing.expect(null == it.previous()); |
| 1689 | try std.testing.expect(null == it.last()); |
| 1690 | try std.testing.expect(null == it.next()); |
| 1691 | } |
| 1692 | } |
| 1693 | |
| 1694 | test "ComponentIterator windows" { |
| 1695 | const WindowsComponentIterator = ComponentIterator(.windows, u8); |
| 1696 | { |
| 1697 | const path = "a/b\\c//"; |
| 1698 | var it = try WindowsComponentIterator.init(path); |
| 1699 | try std.testing.expectEqual(@as(usize, 0), it.root_end_index); |
| 1700 | try std.testing.expect(null == it.root()); |
| 1701 | { |
| 1702 | try std.testing.expect(null == it.previous()); |
| 1703 | |
| 1704 | const first_via_next = it.next().?; |
| 1705 | try std.testing.expectEqualStrings("a", first_via_next.name); |
| 1706 | try std.testing.expectEqualStrings("a", first_via_next.path); |
| 1707 | |
| 1708 | const first = it.first().?; |
| 1709 | try std.testing.expectEqualStrings("a", first.name); |
| 1710 | try std.testing.expectEqualStrings("a", first.path); |
| 1711 | |
| 1712 | try std.testing.expect(null == it.previous()); |
| 1713 | |
| 1714 | const second = it.next().?; |
| 1715 | try std.testing.expectEqualStrings("b", second.name); |
| 1716 | try std.testing.expectEqualStrings("a/b", second.path); |
| 1717 | |
| 1718 | const third = it.next().?; |
| 1719 | try std.testing.expectEqualStrings("c", third.name); |
| 1720 | try std.testing.expectEqualStrings("a/b\\c", third.path); |
| 1721 | |
| 1722 | try std.testing.expect(null == it.next()); |
| 1723 | } |
| 1724 | { |
| 1725 | const last = it.last().?; |
| 1726 | try std.testing.expectEqualStrings("c", last.name); |
| 1727 | try std.testing.expectEqualStrings("a/b\\c", last.path); |
| 1728 | |
| 1729 | try std.testing.expect(null == it.next()); |
| 1730 | |
| 1731 | const second_to_last = it.previous().?; |
| 1732 | try std.testing.expectEqualStrings("b", second_to_last.name); |
| 1733 | try std.testing.expectEqualStrings("a/b", second_to_last.path); |
| 1734 | |
| 1735 | const third_to_last = it.previous().?; |
| 1736 | try std.testing.expectEqualStrings("a", third_to_last.name); |
| 1737 | try std.testing.expectEqualStrings("a", third_to_last.path); |
| 1738 | |
| 1739 | try std.testing.expect(null == it.previous()); |
| 1740 | } |
| 1741 | } |
| 1742 | |
| 1743 | { |
| 1744 | const path = "C:\\a/b/c/"; |
| 1745 | var it = try WindowsComponentIterator.init(path); |
| 1746 | try std.testing.expectEqual(@as(usize, 3), it.root_end_index); |
| 1747 | try std.testing.expectEqualStrings("C:\\", it.root().?); |
| 1748 | { |
| 1749 | const first = it.first().?; |
| 1750 | try std.testing.expectEqualStrings("a", first.name); |
| 1751 | try std.testing.expectEqualStrings("C:\\a", first.path); |
| 1752 | |
| 1753 | const second = it.next().?; |
| 1754 | try std.testing.expectEqualStrings("b", second.name); |
| 1755 | try std.testing.expectEqualStrings("C:\\a/b", second.path); |
| 1756 | |
| 1757 | const third = it.next().?; |
| 1758 | try std.testing.expectEqualStrings("c", third.name); |
| 1759 | try std.testing.expectEqualStrings("C:\\a/b/c", third.path); |
| 1760 | |
| 1761 | try std.testing.expect(null == it.next()); |
| 1762 | } |
| 1763 | { |
| 1764 | const last = it.last().?; |
| 1765 | try std.testing.expectEqualStrings("c", last.name); |
| 1766 | try std.testing.expectEqualStrings("C:\\a/b/c", last.path); |
| 1767 | |
| 1768 | const second_to_last = it.previous().?; |
| 1769 | try std.testing.expectEqualStrings("b", second_to_last.name); |
| 1770 | try std.testing.expectEqualStrings("C:\\a/b", second_to_last.path); |
| 1771 | |
| 1772 | const third_to_last = it.previous().?; |
| 1773 | try std.testing.expectEqualStrings("a", third_to_last.name); |
| 1774 | try std.testing.expectEqualStrings("C:\\a", third_to_last.path); |
| 1775 | |
| 1776 | try std.testing.expect(null == it.previous()); |
| 1777 | } |
| 1778 | } |
| 1779 | |
| 1780 | { |
| 1781 | const path = "/"; |
| 1782 | var it = try WindowsComponentIterator.init(path); |
| 1783 | try std.testing.expectEqual(@as(usize, 1), it.root_end_index); |
| 1784 | try std.testing.expectEqualStrings("/", it.root().?); |
| 1785 | |
| 1786 | try std.testing.expect(null == it.first()); |
| 1787 | try std.testing.expect(null == it.previous()); |
| 1788 | try std.testing.expect(null == it.first()); |
| 1789 | try std.testing.expect(null == it.next()); |
| 1790 | |
| 1791 | try std.testing.expect(null == it.last()); |
| 1792 | try std.testing.expect(null == it.previous()); |
| 1793 | try std.testing.expect(null == it.last()); |
| 1794 | try std.testing.expect(null == it.next()); |
| 1795 | } |
| 1796 | |
| 1797 | { |
| 1798 | const path = ""; |
| 1799 | var it = try WindowsComponentIterator.init(path); |
| 1800 | try std.testing.expectEqual(@as(usize, 0), it.root_end_index); |
| 1801 | try std.testing.expect(null == it.root()); |
| 1802 | |
| 1803 | try std.testing.expect(null == it.first()); |
| 1804 | try std.testing.expect(null == it.previous()); |
| 1805 | try std.testing.expect(null == it.first()); |
| 1806 | try std.testing.expect(null == it.next()); |
| 1807 | |
| 1808 | try std.testing.expect(null == it.last()); |
| 1809 | try std.testing.expect(null == it.previous()); |
| 1810 | try std.testing.expect(null == it.last()); |
| 1811 | try std.testing.expect(null == it.next()); |
| 1812 | } |
| 1813 | } |
| 1814 | |
| 1815 | test "ComponentIterator windows UTF-16" { |
| 1816 | // TODO: Fix on big endian architectures |
| 1817 | if (builtin.cpu.arch.endian() != .Little) { |
| 1818 | return error.SkipZigTest; |
| 1819 | } |
| 1820 | |
| 1821 | const WindowsComponentIterator = ComponentIterator(.windows, u16); |
| 1822 | const L = std.unicode.utf8ToUtf16LeStringLiteral; |
| 1823 | |
| 1824 | const path = L("C:\\a/b/c/"); |
| 1825 | var it = try WindowsComponentIterator.init(path); |
| 1826 | try std.testing.expectEqual(@as(usize, 3), it.root_end_index); |
| 1827 | try std.testing.expectEqualSlices(u16, L("C:\\"), it.root().?); |
| 1828 | { |
| 1829 | const first = it.first().?; |
| 1830 | try std.testing.expectEqualSlices(u16, L("a"), first.name); |
| 1831 | try std.testing.expectEqualSlices(u16, L("C:\\a"), first.path); |
| 1832 | |
| 1833 | const second = it.next().?; |
| 1834 | try std.testing.expectEqualSlices(u16, L("b"), second.name); |
| 1835 | try std.testing.expectEqualSlices(u16, L("C:\\a/b"), second.path); |
| 1836 | |
| 1837 | const third = it.next().?; |
| 1838 | try std.testing.expectEqualSlices(u16, L("c"), third.name); |
| 1839 | try std.testing.expectEqualSlices(u16, L("C:\\a/b/c"), third.path); |
| 1840 | |
| 1841 | try std.testing.expect(null == it.next()); |
| 1842 | } |
| 1843 | { |
| 1844 | const last = it.last().?; |
| 1845 | try std.testing.expectEqualSlices(u16, L("c"), last.name); |
| 1846 | try std.testing.expectEqualSlices(u16, L("C:\\a/b/c"), last.path); |
| 1847 | |
| 1848 | const second_to_last = it.previous().?; |
| 1849 | try std.testing.expectEqualSlices(u16, L("b"), second_to_last.name); |
| 1850 | try std.testing.expectEqualSlices(u16, L("C:\\a/b"), second_to_last.path); |
| 1851 | |
| 1852 | const third_to_last = it.previous().?; |
| 1853 | try std.testing.expectEqualSlices(u16, L("a"), third_to_last.name); |
| 1854 | try std.testing.expectEqualSlices(u16, L("C:\\a"), third_to_last.path); |
| 1855 | |
| 1856 | try std.testing.expect(null == it.previous()); |
| 1857 | } |
| 1858 | } |
| 1859 | |
| 1860 | test "ComponentIterator roots" { |
| 1861 | // UEFI |
| 1862 | { |
| 1863 | var it = try ComponentIterator(.uefi, u8).init("\\\\a"); |
| 1864 | try std.testing.expectEqualStrings("\\\\", it.root().?); |
| 1865 | |
| 1866 | it = try ComponentIterator(.uefi, u8).init("//a"); |
| 1867 | try std.testing.expect(null == it.root()); |
| 1868 | } |
| 1869 | // POSIX |
| 1870 | { |
| 1871 | var it = try ComponentIterator(.posix, u8).init("//a"); |
| 1872 | try std.testing.expectEqualStrings("//", it.root().?); |
| 1873 | |
| 1874 | it = try ComponentIterator(.posix, u8).init("\\\\a"); |
| 1875 | try std.testing.expect(null == it.root()); |
| 1876 | } |
| 1877 | // Windows |
| 1878 | { |
| 1879 | // Drive relative |
| 1880 | var it = try ComponentIterator(.windows, u8).init("C:a"); |
| 1881 | try std.testing.expectEqualStrings("C:", it.root().?); |
| 1882 | |
| 1883 | // Drive absolute |
| 1884 | it = try ComponentIterator(.windows, u8).init("C://a"); |
| 1885 | try std.testing.expectEqualStrings("C://", it.root().?); |
| 1886 | it = try ComponentIterator(.windows, u8).init("C:\\a"); |
| 1887 | try std.testing.expectEqualStrings("C:\\", it.root().?); |
| 1888 | |
| 1889 | // Rooted |
| 1890 | it = try ComponentIterator(.windows, u8).init("\\a"); |
| 1891 | try std.testing.expectEqualStrings("\\", it.root().?); |
| 1892 | it = try ComponentIterator(.windows, u8).init("/a"); |
| 1893 | try std.testing.expectEqualStrings("/", it.root().?); |
| 1894 | |
| 1895 | // Root local device |
| 1896 | it = try ComponentIterator(.windows, u8).init("\\\\."); |
| 1897 | try std.testing.expectEqualStrings("\\\\.", it.root().?); |
| 1898 | it = try ComponentIterator(.windows, u8).init("//?"); |
| 1899 | try std.testing.expectEqualStrings("//?", it.root().?); |
| 1900 | |
| 1901 | // UNC absolute |
| 1902 | it = try ComponentIterator(.windows, u8).init("//"); |
| 1903 | try std.testing.expectEqualStrings("//", it.root().?); |
| 1904 | it = try ComponentIterator(.windows, u8).init("\\\\a"); |
| 1905 | try std.testing.expectEqualStrings("\\\\a", it.root().?); |
| 1906 | it = try ComponentIterator(.windows, u8).init("\\\\a\\b\\\\c"); |
| 1907 | try std.testing.expectEqualStrings("\\\\a\\b\\\\", it.root().?); |
| 1908 | it = try ComponentIterator(.windows, u8).init("//a"); |
| 1909 | try std.testing.expectEqualStrings("//a", it.root().?); |
| 1910 | it = try ComponentIterator(.windows, u8).init("//a/b//c"); |
| 1911 | try std.testing.expectEqualStrings("//a/b//", it.root().?); |
| 1912 | } |
| 1913 | } |