{"id":13822573,"url":"https://github.com/udoprog/relative-path","last_synced_at":"2025-05-15T11:06:19.853Z","repository":{"id":26684752,"uuid":"109530168","full_name":"udoprog/relative-path","owner":"udoprog","description":"Portable relative UTF-8 paths for Rust.","archived":false,"fork":false,"pushed_at":"2025-05-09T01:41:39.000Z","size":241,"stargazers_count":100,"open_issues_count":2,"forks_count":15,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-09T02:27:32.856Z","etag":null,"topics":["path","portable","rust-lang"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/udoprog.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-11-04T21:05:30.000Z","updated_at":"2025-05-09T01:41:25.000Z","dependencies_parsed_at":"2024-12-28T01:02:58.145Z","dependency_job_id":"7d7d2034-5c5f-456e-b96d-bf14ed953b05","html_url":"https://github.com/udoprog/relative-path","commit_stats":{"total_commits":180,"total_committers":9,"mean_commits":20.0,"dds":"0.050000000000000044","last_synced_commit":"3b771e5fe7f8d2a2861f3a5f1307fb27e1af2768"},"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udoprog%2Frelative-path","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udoprog%2Frelative-path/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udoprog%2Frelative-path/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udoprog%2Frelative-path/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/udoprog","download_url":"https://codeload.github.com/udoprog/relative-path/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254328385,"owners_count":22052632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["path","portable","rust-lang"],"created_at":"2024-08-04T08:02:06.768Z","updated_at":"2025-05-15T11:06:19.838Z","avatar_url":"https://github.com/udoprog.png","language":"Rust","readme":"# relative-path\n\n[\u003cimg alt=\"github\" src=\"https://img.shields.io/badge/github-udoprog/relative--path-8da0cb?style=for-the-badge\u0026logo=github\" height=\"20\"\u003e](https://github.com/udoprog/relative-path)\n[\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/relative-path.svg?style=for-the-badge\u0026color=fc8d62\u0026logo=rust\" height=\"20\"\u003e](https://crates.io/crates/relative-path)\n[\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/badge/docs.rs-relative--path-66c2a5?style=for-the-badge\u0026logoColor=white\u0026logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K\" height=\"20\"\u003e](https://docs.rs/relative-path)\n[\u003cimg alt=\"build status\" src=\"https://img.shields.io/github/actions/workflow/status/udoprog/relative-path/ci.yml?branch=main\u0026style=for-the-badge\" height=\"20\"\u003e](https://github.com/udoprog/relative-path/actions?query=branch%3Amain)\n\nPortable relative UTF-8 paths for Rust.\n\nThis crate provides a module analogous to [`std::path`], with the following\ncharacteristics:\n\n* The path separator is set to a fixed character (`/`), regardless of\n  platform.\n* Relative paths cannot represent a path in the filesystem without first\n  specifying *what they are relative to* using functions such as [`to_path`]\n  and [`to_logical_path`].\n* Relative paths are always guaranteed to be valid UTF-8 strings.\n\nOn top of this we support many operations that guarantee the same behavior\nacross platforms.\n\nFor more utilities to manipulate relative paths, see the\n[`relative-path-utils` crate].\n\n\u003cbr\u003e\n\n## Usage\n\nAdd `relative-path` to your `Cargo.toml`:\n\n```toml\nrelative-path = \"2.0.1\"\n```\n\nStart using relative paths:\n\n```rust\nuse serde::{Serialize, Deserialize};\nuse relative_path::RelativePath;\n\n#[derive(Serialize, Deserialize)]\nstruct Manifest\u003c'a\u003e {\n    #[serde(borrow)]\n    source: \u0026'a RelativePath,\n}\n\n```\n\n\u003cbr\u003e\n\n## Serde Support\n\nThis library includes serde support that can be enabled with the `serde`\nfeature.\n\n\u003cbr\u003e\n\n## Why is `std::path` a portability hazard?\n\nPath representations differ across platforms.\n\n* Windows permits using drive volumes (multiple roots) as a prefix (e.g.\n  `\"c:\\\"`) and backslash (`\\`) as a separator.\n* Unix references absolute paths from a single root and uses forward slash\n  (`/`) as a separator.\n\nIf we use `PathBuf`, Storing paths in a manifest would allow our application\nto build and run on one platform but potentially not others.\n\nConsider the following data model and corresponding toml for a manifest:\n\n```rust\nuse std::path::PathBuf;\n\nuse serde::{Serialize, Deserialize};\n\n#[derive(Serialize, Deserialize)]\nstruct Manifest {\n    source: PathBuf,\n}\n```\n\n```toml\nsource = \"C:\\\\Users\\\\udoprog\\\\repo\\\\data\\\\source\"\n```\n\nThis will run for you (assuming `source` exists). So you go ahead and check\nthe manifest into git. The next day your Linux colleague calls you and\nwonders what they have ever done to wrong you?\n\nSo what went wrong? Well two things. You forgot to make the `source`\nrelative, so anyone at the company which has a different username than you\nwon't be able to use it. So you go ahead and fix that:\n\n```toml\nsource = \"data\\\\source\"\n```\n\nBut there is still one problem! A backslash (`\\`) is only a legal path\nseparator on Windows. Luckily you learn that forward slashes are supported\nboth on Windows *and* Linux. So you opt for:\n\n```toml\nsource = \"data/source\"\n```\n\nThings are working now. So all is well... Right? Sure, but we can do better.\n\nThis crate provides types that work with *portable relative paths* (hence\nthe name). So by using [`RelativePath`] we can systematically help avoid\nportability issues like the one above. Avoiding issues at the source is\npreferably over spending 5 minutes of onboarding time on a theoretical\nproblem, hoping that your new hires will remember what to do if they ever\nencounter it.\n\nUsing [`RelativePathBuf`] we can fix our data model like this:\n\n```rust\nuse relative_path::RelativePathBuf;\nuse serde::{Serialize, Deserialize};\n\n#[derive(Serialize, Deserialize)]\npub struct Manifest {\n    source: RelativePathBuf,\n}\n```\n\nAnd where it's used:\n\n```rust\nuse std::fs;\nuse std::env::current_dir;\n\nlet manifest: Manifest = todo!();\n\nlet root = current_dir()?;\nlet source = manifest.source.to_path(\u0026root);\nlet content = fs::read(\u0026source)?;\n```\n\n\u003cbr\u003e\n\n## Overview\n\nConversion to a platform-specific [`Path`] happens through the [`to_path`]\nand [`to_logical_path`] functions. Where you are required to specify the\npath that prefixes the relative path. This can come from a function such as\n[`std::env::current_dir`].\n\n```rust\nuse std::env::current_dir;\nuse std::path::Path;\n\nuse relative_path::RelativePath;\n\nlet root = current_dir()?;\n\n// to_path unconditionally concatenates a relative path with its base:\nlet relative_path = RelativePath::new(\"../foo/./bar\");\nlet full_path = relative_path.to_path(\u0026root);\nassert_eq!(full_path, root.join(\"..\\\\foo\\\\.\\\\bar\"));\n\n// to_logical_path tries to apply the logical operations that the relative\n// path corresponds to:\nlet relative_path = RelativePath::new(\"../foo/./bar\");\nlet full_path = relative_path.to_logical_path(\u0026root);\n\n// Replicate the operation performed by `to_logical_path`.\nlet mut parent = root.clone();\nparent.pop();\nassert_eq!(full_path, parent.join(\"foo\\\\bar\"));\n```\n\nWhen two relative paths are compared to each other, their exact component\nmakeup determines equality.\n\n```rust\nuse relative_path::RelativePath;\n\nassert_ne!(\n    RelativePath::new(\"foo/bar/../baz\"),\n    RelativePath::new(\"foo/baz\")\n);\n```\n\nUsing platform-specific path separators to construct relative paths is not\nsupported.\n\nPath separators from other platforms are simply treated as part of a\ncomponent:\n\n```rust\nuse relative_path::RelativePath;\n\nassert_ne!(\n    RelativePath::new(\"foo/bar\"),\n    RelativePath::new(\"foo\\\\bar\")\n);\n\nassert_eq!(1, RelativePath::new(\"foo\\\\bar\").components().count());\nassert_eq!(2, RelativePath::new(\"foo/bar\").components().count());\n```\n\nTo see if two relative paths are equivalent you can use [`normalize`]:\n\n```rust\nuse relative_path::RelativePath;\n\nassert_eq!(\n    RelativePath::new(\"foo/bar/../baz\").normalize(),\n    RelativePath::new(\"foo/baz\").normalize(),\n);\n```\n\n\u003cbr\u003e\n\n## Additional portability notes\n\nWhile relative paths avoid the most egregious portability issue, that\nabsolute paths will work equally unwell on all platforms. We cannot avoid\nall. This section tries to document additional portability hazards that we\nare aware of.\n\n[`RelativePath`], similarly to [`Path`], makes no guarantees that its\nconstituent components make up legal file names. While components are\nstrictly separated by slashes, we can still store things in them which may\nnot be used as legal paths on all platforms.\n\n* A `NUL` character is not permitted on unix platforms - this is a\n  terminator in C-based filesystem APIs. Slash (`/`) is also used as a path\n  separator.\n* Windows has a number of [reserved characters and names][windows-reserved]\n  (like `CON`, `PRN`, and `AUX`) which cannot legally be part of a\n  filesystem component.\n* Windows paths are [case-insensitive by default][windows-case]. So,\n  `Foo.txt` and `foo.txt` are the same files on windows. But they are\n  considered different paths on most unix systems.\n\nA relative path that *accidentally* contains a platform-specific components\nwill largely result in a nonsensical paths being generated in the hope that\nthey will fail fast during development and testing.\n\n```rust\nuse relative_path::{RelativePath, PathExt};\nuse std::path::Path;\n\nif cfg!(windows) {\n    assert_eq!(\n        Path::new(\"foo\\\\c:\\\\bar\\\\baz\"),\n        RelativePath::new(\"c:\\\\bar\\\\baz\").to_path(\"foo\")\n    );\n}\n\nif cfg!(unix) {\n    assert_eq!(\n        Path::new(\"foo/bar/baz\"),\n        RelativePath::new(\"/bar/baz\").to_path(\"foo\")\n    );\n}\n\nassert_eq!(\n    Path::new(\"foo\").relative_to(\"bar\")?,\n    RelativePath::new(\"../foo\"),\n);\n```\n\n[`None`]: https://doc.rust-lang.org/std/option/enum.Option.html\n[`normalize`]: https://docs.rs/relative-path/1/relative_path/struct.RelativePath.html#method.normalize\n[`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html\n[`RelativePath`]: https://docs.rs/relative-path/1/relative_path/struct.RelativePath.html\n[`RelativePathBuf`]: https://docs.rs/relative-path/1/relative_path/struct.RelativePathBuf.html\n[`std::env::current_dir`]: https://doc.rust-lang.org/std/env/fn.current_dir.html\n[`std::path`]: https://doc.rust-lang.org/std/path/index.html\n[`to_logical_path`]: https://docs.rs/relative-path/1/relative_path/struct.RelativePath.html#method.to_logical_path\n[`to_path`]: https://docs.rs/relative-path/1/relative_path/struct.RelativePath.html#method.to_path\n[windows-reserved]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx\n[windows-case]: https://learn.microsoft.com/en-us/windows/wsl/case-sensitivity\n[`relative-path-utils` crate]: https://docs.rs/relative-path-utils\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fudoprog%2Frelative-path","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fudoprog%2Frelative-path","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fudoprog%2Frelative-path/lists"}