Skip to main content

SourceMap

Struct SourceMap 

Source
pub struct SourceMap { /* private fields */ }
Expand description

A collection of sources laid out end to end in a single global position space.

A SourceMap is the multi-file coordinate layer of a front-end. Each source added to it gets a stable SourceId and a non-overlapping range in one shared position space, so a single global BytePos names a point across the whole project. locate maps such a position back to its (SourceId, local offset) — the inverse of the layout — which is how a diagnostic rendered against a global span knows which file to point at.

§Layout

Sources are placed in the order they are added: the first occupies 0..len₀, the next len₀..len₀ + len₁, and so on. Because the bases only increase, the internal list is always sorted by start offset, so a lookup is a binary search over it — O(log files) — with no separate index to maintain. The whole space is 32 bits wide, the same envelope a single BytePos addresses, so the combined length of every source is capped at u32::MAX; overrunning it is the SpaceExhausted error, never a silent wrap into a neighbour’s range.

§Examples

use source_lang::{BytePos, SourceMap};

let mut map = SourceMap::new();
let main = map.add("main.rs", "fn main() {}").expect("fits"); // global 0..12
let util = map.add("util.rs", "fn helper() {}").expect("fits"); // global 12..26

// A global position resolves to the file it lands in and the local offset.
let (id, local) = map.locate(BytePos::new(13)).expect("inside util.rs");
assert_eq!(id, util);
assert_eq!(local, BytePos::new(1)); // 13 - 12
assert_eq!(map.source(id).unwrap().name(), "util.rs");

// Position 0 is the very start of the first file.
assert_eq!(map.locate(BytePos::new(0)).unwrap().0, main);

// Anything past the last byte belongs to no file.
assert_eq!(map.locate(BytePos::new(26)), None);

Implementations§

Source§

impl SourceMap

Source

pub const fn new() -> Self

Creates an empty map whose global position space starts at 0.

The per-source size ceiling starts at u32::MAX; lower it with set_max_source_len to bound untrusted input.

§Examples
use source_lang::SourceMap;

let map = SourceMap::new();
assert!(map.is_empty());
Source

pub fn with_capacity(capacity: usize) -> Self

Creates an empty map with room for capacity sources preallocated.

A hint only: it sizes the internal list so that adding up to capacity sources does not reallocate, which matters when the source count is known up front. The global position space still starts empty.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::with_capacity(2);
map.add("a", "x").expect("fits");
map.add("b", "y").expect("fits");
assert_eq!(map.len(), 2);
Source

pub const fn max_source_len(&self) -> u32

Returns the current per-source size ceiling, in bytes.

A source longer than this is rejected with SourceMapError::Oversize before it consumes any global space. The default is u32::MAX.

§Examples
use source_lang::SourceMap;

let map = SourceMap::new();
assert_eq!(map.max_source_len(), u32::MAX);
Source

pub fn set_max_source_len(&mut self, max: u32)

Sets the largest a single source may be, in bytes.

Use it to bound how much one untrusted input — a file named on a command line, a buffer from the network — can pull into memory. The limit applies to every later add, add_bytes, and add_file; for a file it is checked against the path’s metadata before any bytes are read. Sources already in the map are unaffected.

§Examples
use source_lang::{SourceMap, SourceMapError};

let mut map = SourceMap::new();
map.set_max_source_len(8);

assert!(map.add("ok", "12345678").is_ok()); // exactly 8 bytes
let err = map.add("big", "123456789").unwrap_err(); // 9 bytes
assert!(matches!(err, SourceMapError::Oversize { len: 9, .. }));
Source

pub fn add( &mut self, name: impl Into<Box<str>>, text: impl Into<Box<str>>, ) -> Result<SourceId, SourceMapError>

Adds a source under name with the given text, returning its SourceId.

The source is appended after every existing one: it takes the range next..next + text.len() where next is the current end of the global space. Both name and text are taken by value (anything that converts into a Box<str> — a String or a &str), so the map owns the text and callers can borrow it back for the life of the map.

Adding an empty text is allowed: it yields a valid id whose source has a zero-width span and does not advance the global space, so it can never be the target of a locate.

§Errors

Returns SourceMapError::SpaceExhausted if text does not fit in the bytes left in the 32-bit global space, or if the map already holds the maximum number of sources. The map is left unchanged, so the failure is recoverable.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::new();
let id = map.add("config.toml", "name = \"demo\"").expect("fits");
assert_eq!(map.source(id).unwrap().text(), "name = \"demo\"");

// A String works just as well as a &str.
let owned = String::from("generated");
let _ = map.add("out.txt", owned).expect("fits");
Source

pub fn add_bytes( &mut self, name: impl Into<Box<str>>, bytes: &[u8], ) -> Result<SourceId, SourceMapError>

Validates raw bytes as UTF-8 and adds them as a source under name.

This is the in-memory counterpart to add_file: both turn untrusted bytes — from a buffer here, from disk there — into a stored source through the same checks, so a network buffer and a file on disk fail and succeed the same way.

§Errors

On any error the map is left unchanged.

§Examples
use source_lang::{SourceMap, SourceMapError};

let mut map = SourceMap::new();
let id = map.add_bytes("greeting.txt", b"hello").expect("valid UTF-8");
assert_eq!(map.source(id).unwrap().text(), "hello");

// A stray binary byte is rejected, not stored as corrupt text.
let err = map.add_bytes("blob", &[0xff]).unwrap_err();
assert!(matches!(err, SourceMapError::NotUtf8 { .. }));
Source

pub fn add_file( &mut self, path: impl AsRef<Path>, ) -> Result<SourceId, SourceMapError>

Available on crate feature std only.

Reads a file from disk and adds its contents as a source named by path.

The file’s size is checked against max_source_len from its metadata before a single byte is read, so an oversize file is rejected without being loaded into memory. The bytes are then validated as UTF-8 and stored. The source’s name is the path as given.

§Errors

On any error the map is left unchanged.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::new();
let id = map.add_file("src/main.rs")?;
assert_eq!(map.source(id).unwrap().name(), "src/main.rs");
Source

pub fn locate(&self, pos: BytePos) -> Option<(SourceId, BytePos)>

Resolves a global position to the source it falls in and the local offset within that source.

The returned BytePos is pos minus the source’s base, i.e. the offset into SourceFile::text. Resolution is a binary search over the sources’ start offsets, so it is O(log files) and borrows the located source rather than copying it.

Returns None when pos belongs to no source: past the end of the last one, or — since a zero-width source contains no position — at the exact offset of an empty source. The membership is half-open: a source covering start..end contains start but not end, so the boundary between two adjacent sources resolves to the second, never to both.

§Examples
use source_lang::{BytePos, SourceMap};

let mut map = SourceMap::new();
let a = map.add("a", "abc").expect("fits");  // 0..3
let b = map.add("b", "de").expect("fits");   // 3..5

assert_eq!(map.locate(BytePos::new(2)), Some((a, BytePos::new(2))));
// The shared boundary at 3 is the start of `b`, not the end of `a`.
assert_eq!(map.locate(BytePos::new(3)), Some((b, BytePos::new(0))));
assert_eq!(map.locate(BytePos::new(5)), None);
Source

pub fn line_col(&self, pos: BytePos) -> Option<(SourceId, LineCol)>

Resolves a global position to its source and 1-based line/column.

This is locate composed with span-lang’s line index: the position is mapped to its source and local offset, then that offset is turned into a LineCol within the source’s own text. The column counts Unicode scalar values, so a multi-byte character advances the column by one, not by its byte width.

Returns None exactly when locate does — for a position past the end of the last source, or at a zero-width source.

Each call builds a line index over the located source, an O(source len) scan. To resolve many positions in the same source, take a reusable index once with SourceFile::line_index instead.

§Examples
use source_lang::{BytePos, LineCol, SourceMap};

let mut map = SourceMap::new();
map.add("a.rs", "fn a() {}").expect("fits"); // 0..9
let b = map.add("b.rs", "let x = 1;\nlet y = 2;").expect("fits"); // 9..30

// Global 20 is the second line of b.rs ("let y = 2;").
let (id, lc) = map.line_col(BytePos::new(20)).expect("in range");
assert_eq!(id, b);
assert_eq!(lc, LineCol::new(2, 1));
Source

pub fn source(&self, id: SourceId) -> Option<&SourceFile>

Borrows the source named by id, or None if the id is not from this map.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::new();
let id = map.add("readme.md", "# title").expect("fits");
assert_eq!(map.source(id).unwrap().name(), "readme.md");
Source

pub fn len(&self) -> usize

Returns the number of sources in the map.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::new();
assert_eq!(map.len(), 0);
map.add("a", "x").expect("fits");
assert_eq!(map.len(), 1);
Source

pub fn is_empty(&self) -> bool

Returns true if the map holds no sources.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::new();
assert!(map.is_empty());
map.add("a", "x").expect("fits");
assert!(!map.is_empty());
Source

pub fn iter( &self, ) -> impl ExactSizeIterator<Item = (SourceId, &SourceFile)> + '_

Iterates over the sources in insertion order, pairing each with its id.

The order is also id order (0, 1, …) and global-offset order, so the iterator walks the global position space from start to end. Useful for listing the loaded files or building a side table keyed by SourceId.

§Examples
use source_lang::SourceMap;

let mut map = SourceMap::new();
map.add("a.txt", "one").expect("fits");
map.add("b.txt", "two").expect("fits");

let names: Vec<_> = map.iter().map(|(_, f)| f.name()).collect();
assert_eq!(names, ["a.txt", "b.txt"]);

Trait Implementations§

Source§

impl Clone for SourceMap

Source§

fn clone(&self) -> SourceMap

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SourceMap

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SourceMap

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for SourceMap

Available on crate feature serde only.
Source§

fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for SourceMap

Source§

impl PartialEq for SourceMap

Source§

fn eq(&self, other: &SourceMap) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for SourceMap

Available on crate feature serde only.
Source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for SourceMap

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.