source_lang/id.rs
1//! Stable handles identifying a source within a map.
2
3/// A small, copyable handle to one source in a [`SourceMap`](crate::SourceMap).
4///
5/// A `SourceId` is a 32-bit index minted by the map when a source is added. It
6/// is stable for the life of the map: the id returned by
7/// [`SourceMap::add`](crate::SourceMap::add) keeps pointing at the same source
8/// no matter how many more are added afterwards, because sources are only ever
9/// appended. That stability is what lets a token, an AST node, or a cached
10/// diagnostic store a `SourceId` and resolve it later.
11///
12/// The id is deliberately opaque — there is no public constructor — so an id can
13/// only come from a map that actually holds the source it names. Pass it back to
14/// [`SourceMap::source`](crate::SourceMap::source) to borrow the source, or to
15/// the result of [`SourceMap::locate`](crate::SourceMap::locate) to identify
16/// where a global position resolved.
17///
18/// # Examples
19///
20/// ```
21/// use source_lang::SourceMap;
22///
23/// let mut map = SourceMap::new();
24/// let first = map.add("a.txt", "alpha").expect("fits");
25/// let second = map.add("b.txt", "beta").expect("fits");
26///
27/// // Ids are assigned in order and stay distinct.
28/// assert_eq!(first.to_u32(), 0);
29/// assert_eq!(second.to_u32(), 1);
30/// assert_ne!(first, second);
31/// ```
32///
33/// With the `serde` feature it serialises transparently as its `u32` index, so a
34/// handle stored in an AST node or a cached diagnostic round-trips on its own.
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub struct SourceId(u32);
38
39impl SourceId {
40 /// Wraps a raw index. Internal: only a map may mint an id, so that every id
41 /// in circulation names a source the map actually holds.
42 #[inline]
43 pub(crate) const fn from_index(index: u32) -> Self {
44 Self(index)
45 }
46
47 /// Returns the raw index this id wraps.
48 ///
49 /// The value is the source's insertion order, starting at `0`. It is useful
50 /// as a dense array key — for a side table of per-source data — but the id
51 /// itself should be preferred wherever an opaque handle will do.
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use source_lang::SourceMap;
57 ///
58 /// let mut map = SourceMap::new();
59 /// let id = map.add("only.txt", "x").expect("fits");
60 /// assert_eq!(id.to_u32(), 0);
61 /// ```
62 #[inline]
63 #[must_use]
64 pub const fn to_u32(self) -> u32 {
65 self.0
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_id_round_trips_through_its_index() {
75 let id = SourceId::from_index(7);
76 assert_eq!(id.to_u32(), 7);
77 }
78
79 #[test]
80 fn test_ids_order_by_index() {
81 assert!(SourceId::from_index(1) < SourceId::from_index(2));
82 assert_eq!(SourceId::from_index(3), SourceId::from_index(3));
83 }
84}