{"id":51514744,"url":"https://github.com/jamesgober/source-lang","last_synced_at":"2026-07-08T10:01:45.570Z","repository":{"id":366208748,"uuid":"1273956228","full_name":"jamesgober/source-lang","owner":"jamesgober","description":"Source file and buffer management with multi-file source maps.","archived":false,"fork":false,"pushed_at":"2026-06-20T18:05:54.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-20T20:03:30.353Z","etag":null,"topics":["compiler","language","parser","rust","source-map"],"latest_commit_sha":null,"homepage":null,"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/jamesgober.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["jamesgober"]}},"created_at":"2026-06-19T03:36:53.000Z","updated_at":"2026-06-20T18:01:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jamesgober/source-lang","commit_stats":null,"previous_names":["jamesgober/source-lang"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/jamesgober/source-lang","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2Fsource-lang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2Fsource-lang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2Fsource-lang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2Fsource-lang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesgober","download_url":"https://codeload.github.com/jamesgober/source-lang/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2Fsource-lang/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35260671,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["compiler","language","parser","rust","source-map"],"created_at":"2026-07-08T10:01:43.653Z","updated_at":"2026-07-08T10:01:45.542Z","avatar_url":"https://github.com/jamesgober.png","language":"Rust","funding_links":["https://github.com/sponsors/jamesgober"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n    \u003cimg width=\"99\" alt=\"Rust logo\" src=\"https://raw.githubusercontent.com/jamesgober/rust-collection/72baabd71f00e14aa9184efcb16fa3deddda3a0a/assets/rust-logo.svg\"\u003e\n    \u003cbr\u003e\n    \u003cb\u003esource-lang\u003c/b\u003e\n    \u003cbr\u003e\n    \u003csub\u003e\u003csup\u003eSOURCE FILES \u0026 MAPS\u003c/sup\u003e\u003c/sub\u003e\n\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://crates.io/crates/source-lang\"\u003e\u003cimg alt=\"Crates.io\" src=\"https://img.shields.io/crates/v/source-lang\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/source-lang\"\u003e\u003cimg alt=\"Downloads\" src=\"https://img.shields.io/crates/d/source-lang?color=%230099ff\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://docs.rs/source-lang\"\u003e\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/docsrs/source-lang\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/jamesgober/source-lang/actions\"\u003e\u003cimg alt=\"CI\" src=\"https://github.com/jamesgober/source-lang/actions/workflows/ci.yml/badge.svg\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/rust-lang/rfcs/blob/master/text/2495-min-rust-version.md\"\u003e\u003cimg alt=\"MSRV\" src=\"https://img.shields.io/badge/MSRV-1.85%2B-blue\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003cbr\u003e\n\n\u003cdiv align=\"left\"\u003e\n    \u003cp\u003e\n        source-lang manages the text a compiler front-end reads: it loads files and buffers, assigns each a stable id, and maps a global position back to the specific file and local offset it came from. It is the multi-file coordinate layer that sits above raw spans, so a diagnostic can say not just where in a buffer an error is, but which file.\n    \u003c/p\u003e\n    \u003cbr\u003e\n    \u003chr\u003e\n    \u003cp\u003e\n        \u003cstrong\u003eMSRV is 1.85+\u003c/strong\u003e (Rust 2024 edition).\n    \u003c/p\u003e\n    \u003cblockquote\u003e\n        \u003cstrong\u003eStatus: stable (1.0).\u003c/strong\u003e The public API is frozen and follows Semantic Versioning \u0026mdash; no breaking changes before \u003ccode\u003e2.0\u003c/code\u003e. See \u003ca href=\"./docs/API.md#semver-promise\"\u003e\u003ccode\u003ethe SemVer promise\u003c/code\u003e\u003c/a\u003e and \u003ca href=\"./CHANGELOG.md\"\u003e\u003ccode\u003eCHANGELOG.md\u003c/code\u003e\u003c/a\u003e.\n    \u003c/blockquote\u003e\n\u003c/div\u003e\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Installation\n\n```toml\n[dependencies]\nsource-lang = \"1.0\"\n```\n\nOr from the terminal:\n\n```bash\ncargo add source-lang\n```\n\n\u003cbr\u003e\n\n## Usage\n\nAdd sources to a map and resolve a global position back to the file and local\noffset it came from.\n\n```rust\nuse source_lang::{BytePos, SourceMap};\n\nlet mut map = SourceMap::new();\nlet main = map.add(\"main.rs\", \"fn main() {}\")?;   // global 0..12\nlet util = map.add(\"util.rs\", \"fn helper() {}\")?; // global 12..26\n\n// Which file does global position 13 belong to, and where inside it?\nlet (id, local) = map.locate(BytePos::new(13)).expect(\"inside util.rs\");\nassert_eq!(id, util);\nassert_eq!(local, BytePos::new(1)); // 13 - 12\n\n// The id is a stable handle back to the source for the life of the map.\nassert_eq!(map.source(main).unwrap().name(), \"main.rs\");\n# Ok::\u003c(), source_lang::SourceMapError\u003e(())\n```\n\nRead the located text back out of the resolved source:\n\n```rust\nuse source_lang::{BytePos, SourceMap};\n\nlet mut map = SourceMap::new();\nmap.add(\"a\", \"let x = 1;\")?;\nlet two = map.add(\"b\", \"let y = 2;\")?;\n\nlet (id, local) = map.locate(BytePos::new(14)).expect(\"in range\");\nassert_eq!(id, two);\nlet file = map.source(id).unwrap();\nassert_eq!(\u0026file.text()[local.to_usize()..], \"y = 2;\");\n# Ok::\u003c(), source_lang::SourceMapError\u003e(())\n```\n\nResolve a global position to its file and 1-based line/column in one step — what a\ndiagnostic renderer needs to print `file:line:col`:\n\n```rust\nuse source_lang::{BytePos, LineCol, SourceMap};\n\nlet mut map = SourceMap::new();\nmap.add(\"a.rs\", \"fn a() {}\")?;                  // global 0..9\nlet b = map.add(\"b.rs\", \"let x = 1;\\nlet y = 2;\")?; // global 9..30\n\nlet (id, lc) = map.line_col(BytePos::new(20)).expect(\"in range\");\nassert_eq!(id, b);\nassert_eq!(lc, LineCol::new(2, 1)); // second line of b.rs\n# Ok::\u003c(), source_lang::SourceMapError\u003e(())\n```\n\nLoad untrusted input — a file from disk or raw bytes from a buffer — through the\nsame checks, so bad input is a defined error rather than a panic:\n\n```rust\nuse source_lang::{SourceMap, SourceMapError};\n\nlet mut map = SourceMap::new();\nmap.set_max_source_len(1 \u003c\u003c 20); // cap any single source at 1 MiB\n\n// Raw bytes are validated as UTF-8 before they are stored.\nlet id = map.add_bytes(\"config.toml\", b\"name = \\\"demo\\\"\")?;\nassert_eq!(map.source(id).unwrap().text(), \"name = \\\"demo\\\"\");\n\n// Non-UTF-8 input is rejected, naming the source.\nlet err = map.add_bytes(\"blob.bin\", \u0026[0xff, 0xfe]).unwrap_err();\nassert!(matches!(err, SourceMapError::NotUtf8 { .. }));\n# Ok::\u003c(), source_lang::SourceMapError\u003e(())\n```\n\nWith the default `std` feature, `map.add_file(\"src/main.rs\")` reads a path from\ndisk through those same checks, rejecting an oversize file from its metadata before\na byte is read.\n\nWalk every loaded source in order — id order is also global-offset order:\n\n```rust\nuse source_lang::SourceMap;\n\nlet mut map = SourceMap::new();\nmap.add(\"a.txt\", \"one\")?;\nmap.add(\"b.txt\", \"two\")?;\n\nlet names: Vec\u003c_\u003e = map.iter().map(|(_, f)| f.name()).collect();\nassert_eq!(names, [\"a.txt\", \"b.txt\"]);\n# Ok::\u003c(), source_lang::SourceMapError\u003e(())\n```\n\nWith the `serde` feature, a whole `SourceMap` round-trips through any serde format —\nits spans and ids are regenerated on load, so the layout is validated rather than\ntrusted:\n\n```rust,ignore\nlet mut map = SourceMap::new();\nmap.add(\"main.rs\", \"fn main() {}\")?;\nlet json = serde_json::to_string(\u0026map)?;\nlet restored: SourceMap = serde_json::from_str(\u0026json)?;\nassert_eq!(map, restored);\n```\n\nSee \u003ca href=\"./docs/API.md\"\u003e\u003ccode\u003edocs/API.md\u003c/code\u003e\u003c/a\u003e for the full reference.\n\n\u003cbr\u003e\n\n## How it works\n\nSources are placed end to end in the order they are added: the first occupies\nglobal offsets `0..len₀`, the next `len₀..len₀ + len₁`, and so on. The ranges never\noverlap, and because each base is the running total of all earlier sources, the\ninternal list stays sorted by offset — so \u003ccode\u003elocate\u003c/code\u003e is a binary search,\n\u003ccode\u003eO(log files)\u003c/code\u003e, that borrows the resolved source rather than copying it.\nThe shared space is 32 bits wide (the same envelope a single \u003ccode\u003eBytePos\u003c/code\u003e\naddresses), so the combined length of every source is capped at 4\u0026nbsp;GiB;\noverrunning it is a defined error, never a silent wrap into a neighbour's range.\n\n\u003cbr\u003e\n\n## Status\n\n\u003ccode\u003ev1.0.0\u003c/code\u003e is the stable release: the public API is frozen and follows\nSemantic Versioning, with no breaking changes before \u003ccode\u003e2.0\u003c/code\u003e. The surface is\nthe full source map \u0026mdash; the \u003ccode\u003eSourceMap\u003c/code\u003e, stable \u003ccode\u003eSourceId\u003c/code\u003es,\nand the non-overlapping global position space with its \u003ccode\u003eO(log files)\u003c/code\u003e\nresolver, disk and buffer loading, \u003ccode\u003eline_col\u003c/code\u003e resolution, and optional\n\u003ccode\u003eserde\u003c/code\u003e \u0026mdash; each invariant property-tested against a naive linear\nscan, verified on Linux, macOS, and Windows. See the\n\u003ca href=\"./docs/API.md#semver-promise\"\u003e\u003ccode\u003eSemVer promise\u003c/code\u003e\u003c/a\u003e.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Contributing\n\nSee \u003ca href=\"./dev/DIRECTIVES.md\"\u003e\u003ccode\u003edev/DIRECTIVES.md\u003c/code\u003e\u003c/a\u003e for engineering standards and the definition of done. Before a PR: `cargo fmt --all`, `cargo clippy --all-targets --all-features -- -D warnings`, and `cargo test --all-features` must be clean.\n\n\u003cbr\u003e\n\n\u003cdiv id=\"license\"\u003e\n    \u003ch2\u003eLicense\u003c/h2\u003e\n    \u003cp\u003eLicensed under either of\u003c/p\u003e\n    \u003cul\u003e\n        \u003cli\u003e\u003cb\u003eApache License, Version 2.0\u003c/b\u003e \u0026mdash; \u003ca href=\"./LICENSE-APACHE\"\u003eLICENSE-APACHE\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003cb\u003eMIT License\u003c/b\u003e \u0026mdash; \u003ca href=\"./LICENSE-MIT\"\u003eLICENSE-MIT\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n    \u003cp\u003eat your option.\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ch2\u003e\u003c/h2\u003e\n  \u003csup\u003eCOPYRIGHT \u003csmall\u003e\u0026copy;\u003c/small\u003e 2026 \u003cstrong\u003eJames Gober \u003cme@jamesgober.com\u003e.\u003c/strong\u003e\u003c/sup\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesgober%2Fsource-lang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesgober%2Fsource-lang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesgober%2Fsource-lang/lists"}