Skip to main content

wgpu_types/
compilation_info.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3
4#[cfg(any(feature = "serde", test))]
5use serde::{Deserialize, Serialize};
6
7/// Compilation information for a shader module.
8///
9/// Corresponds to [WebGPU `GPUCompilationInfo`](https://gpuweb.github.io/gpuweb/#gpucompilationinfo).
10/// The source locations use bytes, and index a UTF-8 or UTF-16 encoded string.
11#[derive(Debug, Clone)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct CompilationInfo<SL = SourceLocation> {
14    /// The messages from the shader compilation process.
15    pub messages: Vec<CompilationMessage<SL>>,
16}
17
18impl<SL> Default for CompilationInfo<SL> {
19    fn default() -> Self {
20        Self {
21            messages: Default::default(),
22        }
23    }
24}
25
26/// A single message from the shader compilation process.
27///
28/// Roughly corresponds to [`GPUCompilationMessage`](https://www.w3.org/TR/webgpu/#gpucompilationmessage),
29/// except that the location may use UTF-8 for all positions.
30#[derive(Debug, Clone)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32pub struct CompilationMessage<SL = SourceLocation> {
33    /// The text of the message.
34    pub message: String,
35    /// The type of the message.
36    pub message_type: CompilationMessageType,
37    /// Where in the source code the message points at.
38    pub location: Option<SL>,
39}
40
41/// The type of a compilation message.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[repr(u8)]
45pub enum CompilationMessageType {
46    /// An error message.
47    Error,
48    /// A warning message.
49    Warning,
50    /// An informational message.
51    Info,
52}
53
54/// A human-readable representation for a span, tailored for text source.
55///
56/// Roughly corresponds to the positional members of [`GPUCompilationMessage`][gcm] from
57/// the WebGPU specification, except
58/// - `offset` and `length` are in bytes (UTF-8 code units), instead of UTF-16 code units.
59/// - `line_position` is in bytes (UTF-8 code units), and is usually not directly intended for humans.
60///
61/// For UTF-16 code units, see [`Utf16SourceLocation`].
62///
63/// [gcm]: https://www.w3.org/TR/webgpu/#gpucompilationmessage
64#[derive(Copy, Clone, Debug, PartialEq, Eq)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66pub struct SourceLocation {
67    /// 1-based line number.
68    pub line_number: u32,
69    /// 1-based column in code units (in bytes) of the start of the span.
70    /// Remember to convert accordingly when displaying to the user.
71    pub line_position: u32,
72    /// 0-based offset in code units (in bytes) of the start of the span.
73    pub offset: u32,
74    /// Length in code units (in bytes) of the span.
75    pub length: u32,
76}
77
78/// A human-readable representation for a span, tailored for text source.
79///
80/// Corresponds to the positional members of [`GPUCompilationMessage`][gcm] from
81/// the WebGPU specification.
82///
83/// Unlike [`SourceLocation`], which uses UTF-8 units as a convenience for the
84/// `wgpu` Rust API, this struct locates the span using UTF-16 code units, just
85/// like the WebGPU specification.
86///
87/// [gcm]: https://www.w3.org/TR/webgpu/#gpucompilationmessage
88#[derive(Copy, Clone, Debug, PartialEq, Eq)]
89#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
90pub struct Utf16SourceLocation {
91    /// 1-based line number.
92    pub line_number: u32,
93    /// 1-based column in UTF-16 code units of the start of the span.
94    /// Remember to convert accordingly when displaying to the user.
95    pub line_position: u32,
96    /// 0-based offset in UTF-16 code units of the start of the span.
97    pub offset: u32,
98    /// Length in UTF-16 code units of the span.
99    pub length: u32,
100}
101
102impl SourceLocation {
103    /// Converts a `SourceLocation` in UTF-8 code units to UTF-16 code units.
104    pub fn to_utf16(&self, source: &str) -> Utf16SourceLocation {
105        let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u32).sum::<u32>();
106        let start = self.offset as usize;
107        let end = start + self.length as usize;
108        let utf16_offset = len_utf16(&source[..start]);
109        let utf16_length = len_utf16(&source[start..end]);
110
111        let line_start = source[..start].rfind('\n').map_or(0, |pos| pos + 1);
112        let utf16_line_position = len_utf16(&source[line_start..start]) + 1;
113
114        Utf16SourceLocation {
115            line_number: self.line_number,
116            line_position: utf16_line_position,
117            offset: utf16_offset,
118            length: utf16_length,
119        }
120    }
121}
122
123impl Utf16SourceLocation {
124    /// Converts a `SourceLocation` in UTF-16 code units to UTF-8 code units.
125    pub fn to_utf8(&self, source: &str) -> SourceLocation {
126        fn map_utf16_to_utf8_offset(utf16_offset: u32, text: &str) -> u32 {
127            let mut utf16_i = 0;
128            for (utf8_index, c) in text.char_indices() {
129                if utf16_i >= utf16_offset {
130                    return utf8_index as u32;
131                }
132                utf16_i += c.len_utf16() as u32;
133            }
134            if utf16_i >= utf16_offset {
135                text.len() as u32
136            } else {
137                log::error!("UTF16 offset {utf16_offset} is out of bounds for string {text}");
138                u32::MAX
139            }
140        }
141        let utf8_offset = map_utf16_to_utf8_offset(self.offset, source);
142        let utf8_length = map_utf16_to_utf8_offset(self.length, &source[utf8_offset as usize..]);
143
144        let prefix = &source[..utf8_offset as usize];
145        let line_start = prefix.rfind('\n').map(|pos| pos + 1).unwrap_or(0) as u32;
146        let utf8_line_position = utf8_offset - line_start + 1; // Counting UTF-8 bytes
147
148        SourceLocation {
149            line_number: self.line_number,
150            line_position: utf8_line_position,
151            offset: utf8_offset,
152            length: utf8_length,
153        }
154    }
155}