Repository: Nicolas-Ferre/wgso
Pull request #49: Add imports with relative path
Author: login: Nicolas-Ferre
Labels: name: enhancement
description: New feature or request
Created: 2025-05-04T09:29:23Z | Merged: 2025-05-04T09:42:08Z

Closes #41 

Linked issue #41: Add imports with relative path
Allow relative paths in `#import` directives to facilitate reusability:
```wgsl
#import folder.file # absolute import: <root path>/folder/file.wgsl
#import ~.folder.file # relative import: <current file path>/../folder/file.wgsl
#import ~.~.folder.file # relative import: <current file path>/../../folder/file.wgsl
```

Any number of `~` should be supported.


Diff:
diff --git a/crates/wgso/config/directives.yaml b/crates/wgso/config/directives.yaml
index 0604968..1c5b8a6 100644
--- a/crates/wgso/config/directives.yaml
+++ b/crates/wgso/config/directives.yaml
@@ -1,7 +1,4 @@
 patterns:
-  ident: &ident
-    label: identifier
-    regex: '[a-zA-Z_][a-zA-Z0-9_]*'
   i32: &i32
     label: '`i32` value'
     regex: '-?[0-9]+'
@@ -12,6 +9,12 @@ patterns:
     regex: '[0-9]+'
     min: 0
     max: 65535
+  ident: &ident
+    label: identifier
+    regex: '[a-zA-Z_][a-zA-Z0-9_]*'
+  parent_folder: &parent_folder
+    label: "`~`"
+    regex: '~'
 
 priority: &priority !repeat
   max: 1
@@ -142,6 +145,12 @@ draw: &draw
   - !token ')'
 
 import: &import
+  - !repeat
+    group:
+      - !pattern
+        label: import_segment
+        config: *parent_folder
+      - !token '.'
   - !pattern
     label: import_segment
     config: *ident
diff --git a/crates/wgso/src/directives/imports.rs b/crates/wgso/src/directives/imports.rs
index 8cef300..5e23681 100644
--- a/crates/wgso/src/directives/imports.rs
+++ b/crates/wgso/src/directives/imports.rs
@@ -5,18 +5,30 @@ use std::path::{Path, PathBuf};
 use wgso_parser::ParsingError;
 
 impl Directive {
-    pub(crate) fn import_path(&self) -> PathBuf {
+    pub(crate) fn import_path(&self, root_path: &Path) -> PathBuf {
         let segment_count = self.find_all_by_label("import_segment").count();
+        let is_relative = self
+            .find_all_by_label("import_segment")
+            .next()
+            .is_some_and(|segment| segment.slice == "~");
+        let root_path = if is_relative {
+            let file_path = self.path();
+            file_path.parent().unwrap_or(file_path).to_path_buf()
+        } else {
+            root_path.to_path_buf()
+        };
         self.find_all_by_label("import_segment")
             .enumerate()
-            .map(|(index, segment)| {
+            .filter(|(index, segment)| *index != 0 || segment.slice != "~")
+            .fold(root_path, |path, (index, segment)| {
                 if index == segment_count - 1 {
-                    format!("{}.wgsl", segment.slice.clone())
+                    path.join(format!("{}.wgsl", segment.slice))
+                } else if segment.slice == "~" {
+                    path.parent().map(Path::to_path_buf).unwrap_or(path)
                 } else {
-                    segment.slice.clone()
+                    path.join(&segment.slice)
                 }
             })
-            .collect::<PathBuf>()
     }
 }
 
@@ -28,7 +40,7 @@ pub(crate) fn check(
 ) {
     for directive in directives {
         if directive.kind() == DirectiveKind::Import {
-            let path = root_path.join(directive.import_path());
+            let path = directive.import_path(root_path);
             if !files.exists(&path) {
                 errors.push(Error::DirectiveParsing(ParsingError {
                     path: directive.path().into(),
diff --git a/crates/wgso/src/program/module.rs b/crates/wgso/src/program/module.rs
index cd8a145..e052bc9 100644
--- a/crates/wgso/src/program/module.rs
+++ b/crates/wgso/src/program/module.rs
@@ -175,7 +175,7 @@ impl Module {
                     DirectiveKind::Import,
                 );
                 for directive in import_directives {
-                    paths.insert(root_path.join(directive.import_path()));
+                    paths.insert(directive.import_path(root_path));
                 }
             }
         }
diff --git a/crates/wgso/tests/cases_valid/imports/.expected b/crates/wgso/tests/cases_valid/imports/.expected
index 51afef3..b46bb5e 100644
--- a/crates/wgso/tests/cases_valid/imports/.expected
+++ b/crates/wgso/tests/cases_valid/imports/.expected
@@ -8,4 +8,6 @@ target=[
     000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 
     000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 000000FF, 
 ]
-counter=[1, 0, 0, 0]
\ No newline at end of file
+counter=[1, 0, 0, 0]
+relative1=[2, 0, 0, 0]
+relative2=[3, 0, 0, 0]
\ No newline at end of file
diff --git a/crates/wgso/tests/cases_valid/imports/relative/inner/relative.wgsl b/crates/wgso/tests/cases_valid/imports/relative/inner/relative.wgsl
new file mode 100644
index 0000000..d93a0e4
--- /dev/null
+++ b/crates/wgso/tests/cases_valid/imports/relative/inner/relative.wgsl
@@ -0,0 +1,15 @@
+#shader<compute> use_relative_import
+#run use_relative_import()
+
+#import ~.side
+#import ~.~.~.root
+
+var<storage, read_write> relative1: u32;
+var<storage, read_write> relative2: u32;
+
+@compute
+@workgroup_size(1, 1, 1)
+fn main() {
+    relative1 = CONSTANT1;
+    relative2 = CONSTANT2;
+}
diff --git a/crates/wgso/tests/cases_valid/imports/relative/inner/side.wgsl b/crates/wgso/tests/cases_valid/imports/relative/inner/side.wgsl
new file mode 100644
index 0000000..b1e9190
--- /dev/null
+++ b/crates/wgso/tests/cases_valid/imports/relative/inner/side.wgsl
@@ -0,0 +1 @@
+const CONSTANT2 = 3;
\ No newline at end of file
diff --git a/crates/wgso/tests/cases_valid/imports/root.wgsl b/crates/wgso/tests/cases_valid/imports/root.wgsl
new file mode 100644
index 0000000..7b8047a
--- /dev/null
+++ b/crates/wgso/tests/cases_valid/imports/root.wgsl
@@ -0,0 +1 @@
+const CONSTANT1 = 2;
\ No newline at end of file
diff --git a/examples/triangles/background/shaders/draw.wgsl b/examples/triangles/background/draw.wgsl
similarity index 88%
rename from examples/triangles/background/shaders/draw.wgsl
rename to examples/triangles/background/draw.wgsl
index 8f31d35..864b282 100644
--- a/examples/triangles/background/shaders/draw.wgsl
+++ b/examples/triangles/background/draw.wgsl
@@ -1,7 +1,7 @@
 #shader<render, Vertex, Background> background
 
-#import vertices.types
-#import background.types
+#import ~.main
+#import vertices.main
 
 struct Fragment {
     @builtin(position)
diff --git a/examples/triangles/background/main.wgsl b/examples/triangles/background/main.wgsl
index ff533ce..8402370 100644
--- a/examples/triangles/background/main.wgsl
+++ b/examples/triangles/background/main.wgsl
@@ -1,2 +1,6 @@
 #run update_background()
 #draw background<vertices.rectangle, background.instance>()
+
+struct Background {
+    color: vec4f,
+}
diff --git a/examples/triangles/background/storages.wgsl b/examples/triangles/background/storages.wgsl
index 50b970b..ef018a9 100644
--- a/examples/triangles/background/storages.wgsl
+++ b/examples/triangles/background/storages.wgsl
@@ -1,3 +1,7 @@
-#import background.types
+#import ~.main
 
 var<storage, read_write> background: BackgroundState;
+
+struct BackgroundState {
+    instance: Background,
+}
diff --git a/examples/triangles/background/types.wgsl b/examples/triangles/background/types.wgsl
deleted file mode 100644
index 3000e51..0000000
--- a/examples/triangles/background/types.wgsl
+++ /dev/null
@@ -1,7 +0,0 @@
-struct BackgroundState {
-    instance: Background,
-}
-
-struct Background {
-    color: vec4f,
-}
diff --git a/examples/triangles/background/shaders/update.wgsl b/examples/triangles/background/update.wgsl
similarity index 93%
rename from examples/triangles/background/shaders/update.wgsl
rename to examples/triangles/background/update.wgsl
index 1a6cf6c..fb4ca74 100644
--- a/examples/triangles/background/shaders/update.wgsl
+++ b/examples/triangles/background/update.wgsl
@@ -1,7 +1,7 @@
 #shader<compute> update_background
 
+#import ~.storages
 #import constants
-#import background.storages
 #import triangles.storages
 
 @compute
diff --git a/examples/triangles/triangles/shaders/draw.wgsl b/examples/triangles/triangles/draw.wgsl
similarity index 93%
rename from examples/triangles/triangles/shaders/draw.wgsl
rename to examples/triangles/triangles/draw.wgsl
index d49264d..f8319ab 100644
--- a/examples/triangles/triangles/shaders/draw.wgsl
+++ b/examples/triangles/triangles/draw.wgsl
@@ -1,7 +1,7 @@
 #shader<render, Vertex, Triangle> triangle
 
-#import vertices.types
-#import triangles.types
+#import ~.main
+#import vertices.main
 
 struct Fragment {
     @builtin(position)
diff --git a/examples/triangles/triangles/shaders/init.wgsl b/examples/triangles/triangles/init.wgsl
similarity index 90%
rename from examples/triangles/triangles/shaders/init.wgsl
rename to examples/triangles/triangles/init.wgsl
index 35f2adc..1c32a9e 100644
--- a/examples/triangles/triangles/shaders/init.wgsl
+++ b/examples/triangles/triangles/init.wgsl
@@ -1,6 +1,6 @@
 #shader<compute> init_triangles
 
-#import triangles.storages
+#import ~.storages
 
 @compute
 @workgroup_size(1, 1, 1)
diff --git a/examples/triangles/triangles/main.wgsl b/examples/triangles/triangles/main.wgsl
index 0bddc9d..97b9c73 100644
--- a/examples/triangles/triangles/main.wgsl
+++ b/examples/triangles/triangles/main.wgsl
@@ -1,3 +1,12 @@
 #init init_triangles()
 #run update_triangles()
 #draw triangle<vertices.triangle, triangles.instances>()
+
+struct Triangle {
+    position: vec2f,
+    brightness_param: f32,
+}
+
+fn triangle_brightness(brightness_param: f32) -> f32 {
+    return (sin(brightness_param / 3.14 * 5) + 0.5) / 2 + 0.5;
+}
diff --git a/examples/triangles/triangles/storages.wgsl b/examples/triangles/triangles/storages.wgsl
index b04fe3b..ed871c5 100644
--- a/examples/triangles/triangles/storages.wgsl
+++ b/examples/triangles/triangles/storages.wgsl
@@ -1,3 +1,9 @@
-#import triangles.types
+#import ~.main
+
+const TRIANGLE_COUNT = 3;
 
 var<storage, read_write> triangles: TriangleState;
+
+struct TriangleState {
+    instances: array<Triangle, TRIANGLE_COUNT>,
+}
diff --git a/examples/triangles/triangles/types.wgsl b/examples/triangles/triangles/types.wgsl
deleted file mode 100644
index fa1349d..0000000
--- a/examples/triangles/triangles/types.wgsl
+++ /dev/null
@@ -1,14 +0,0 @@
-const TRIANGLE_COUNT = 3;
-
-struct TriangleState {
-    instances: array<Triangle, TRIANGLE_COUNT>,
-}
-
-struct Triangle {
-    position: vec2f,
-    brightness_param: f32,
-}
-
-fn triangle_brightness(brightness_param: f32) -> f32 {
-    return (sin(brightness_param / 3.14 * 5) + 0.5) / 2 + 0.5;
-}
diff --git a/examples/triangles/triangles/shaders/update.wgsl b/examples/triangles/triangles/update.wgsl
similarity index 90%
rename from examples/triangles/triangles/shaders/update.wgsl
rename to examples/triangles/triangles/update.wgsl
index b1632bb..e8edd85 100644
--- a/examples/triangles/triangles/shaders/update.wgsl
+++ b/examples/triangles/triangles/update.wgsl
@@ -1,7 +1,7 @@
 #shader<compute> update_triangles
 
+#import ~.storages
 #import constants
-#import triangles.storages
 
 @compute
 @workgroup_size(TRIANGLE_COUNT, 1, 1)
diff --git a/examples/triangles/vertices/shaders/init.wgsl b/examples/triangles/vertices/init.wgsl
similarity index 94%
rename from examples/triangles/vertices/shaders/init.wgsl
rename to examples/triangles/vertices/init.wgsl
index 6339b0b..6c2a848 100644
--- a/examples/triangles/vertices/shaders/init.wgsl
+++ b/examples/triangles/vertices/init.wgsl
@@ -1,6 +1,6 @@
 #shader<compute> init_vertices
 
-#import vertices.storages
+#import ~.storages
 
 @compute
 @workgroup_size(1, 1, 1)
diff --git a/examples/triangles/vertices/main.wgsl b/examples/triangles/vertices/main.wgsl
index 4e7cf5a..12b4aeb 100644
--- a/examples/triangles/vertices/main.wgsl
+++ b/examples/triangles/vertices/main.wgsl
@@ -1 +1,5 @@
 #init init_vertices()
+
+struct Vertex {
+    position: vec2f,
+}
diff --git a/examples/triangles/vertices/storages.wgsl b/examples/triangles/vertices/storages.wgsl
index df4af02..5003b91 100644
--- a/examples/triangles/vertices/storages.wgsl
+++ b/examples/triangles/vertices/storages.wgsl
@@ -1,3 +1,8 @@
-#import vertices.types
+#import ~.main
 
 var<storage, read_write> vertices: VertexState;
+
+struct VertexState {
+    triangle: array<Vertex, 3>,
+    rectangle: array<Vertex, 6>,
+}
diff --git a/examples/triangles/vertices/types.wgsl b/examples/triangles/vertices/types.wgsl
deleted file mode 100644
index 58a2d10..0000000
--- a/examples/triangles/vertices/types.wgsl
+++ /dev/null
@@ -1,8 +0,0 @@
-struct VertexState {
-    triangle: array<Vertex, 3>,
-    rectangle: array<Vertex, 6>,
-}
-
-struct Vertex {
-    position: vec2f,
-}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>crates/wgso/tests/cases_valid/imports/relative/inner/side.wgsl (1)</summary><blockquote>

`1-1`: **Added constant is correct and test-aligned.**

The declaration `const CONSTANT2 = 3;` matches the new relative import test and aligns with the expected output in `.expected`.  
Ensure there’s a trailing newline at end-of-file for POSIX compliance.

</blockquote></details>
<details>
<summary>crates/wgso/tests/cases_valid/imports/root.wgsl (1)</summary><blockquote>

`1-1`: **Added root constant for relative imports.**

The new `const CONSTANT1 = 2;` declaration supports the relative import test cases and initializes the constant correctly.  
Ensure a trailing newline at end-of-file for consistency.

</blockquote></details>
<details>
<summary>crates/wgso/config/directives.yaml (1)</summary><blockquote>

`15-17`: **Be explicit to avoid YAML’s `~` null-literal pitfall**  

Although the single quotes around `~` protect you here, YAML interprets an unquoted `~` as `null`. If another contributor later edits this file and removes the quotes by mistake, the directive will silently become invalid. Consider adding a short comment or renaming the anchor to something less error-prone (e.g. `parent_dir_token`) to guard against that scenario.

</blockquote></details>
<details>
<summary>crates/wgso/src/directives/imports.rs (1)</summary><blockquote>

`23-31`: **Micro-optimisation & readability**  

`self.find_all_by_label("import_segment")` is executed three times (count, first element, fold). Collecting once avoids repeated tree walks and eliminates the `segment_count` / `index` gymnastics:

```diff
-        let segment_count = self.find_all_by_label("import_segment").count();
-        let is_relative = …
-        self.find_all_by_label("import_segment")
-            .enumerate()
-            .filter(|(index, segment)| *index != 0 || segment.slice != "~")
+        let segments: Vec<_> = self.find_all_by_label("import_segment").collect();
+        let is_relative = segments.first().is_some_and(|s| s.slice == "~");
+        segments
+            .iter()
+            .enumerate()
+            .filter(|(i, s)| *i != 0 || s.slice != "~")
```

Not critical, but it simplifies the branch logic and improves performance for long import chains.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fac5de8d7a9692d281c6ad4c37f86f6010ba6628 and f949ff3eb626adcdfb41f103bb0f668df4ab93b5.

</details>

<details>
<summary>📒 Files selected for processing (22)</summary>

* `crates/wgso/config/directives.yaml` (2 hunks)
* `crates/wgso/src/directives/imports.rs` (2 hunks)
* `crates/wgso/src/program/module.rs` (1 hunks)
* `crates/wgso/tests/cases_valid/imports/.expected` (1 hunks)
* `crates/wgso/tests/cases_valid/imports/relative/inner/relative.wgsl` (1 hunks)
* `crates/wgso/tests/cases_valid/imports/relative/inner/side.wgsl` (1 hunks)
* `crates/wgso/tests/cases_valid/imports/root.wgsl` (1 hunks)
* `examples/triangles/background/draw.wgsl` (1 hunks)
* `examples/triangles/background/main.wgsl` (1 hunks)
* `examples/triangles/background/storages.wgsl` (1 hunks)
* `examples/triangles/background/types.wgsl` (0 hunks)
* `examples/triangles/background/update.wgsl` (1 hunks)
* `examples/triangles/triangles/draw.wgsl` (1 hunks)
* `examples/triangles/triangles/init.wgsl` (1 hunks)
* `examples/triangles/triangles/main.wgsl` (1 hunks)
* `examples/triangles/triangles/storages.wgsl` (1 hunks)
* `examples/triangles/triangles/types.wgsl` (0 hunks)
* `examples/triangles/triangles/update.wgsl` (1 hunks)
* `examples/triangles/vertices/init.wgsl` (1 hunks)
* `examples/triangles/vertices/main.wgsl` (1 hunks)
* `examples/triangles/vertices/storages.wgsl` (1 hunks)
* `examples/triangles/vertices/types.wgsl` (0 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (3)</summary>

* examples/triangles/background/types.wgsl
* examples/triangles/triangles/types.wgsl
* examples/triangles/vertices/types.wgsl

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>crates/wgso/src/directives/imports.rs (1)</summary><blockquote>

<details>
<summary>crates/wgso/src/error.rs (1)</summary>

* `path` (60-71)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (20)</summary><blockquote>

<details>
<summary>examples/triangles/triangles/main.wgsl (2)</summary>

`4-12`: **Well-defined Triangle struct with clear field semantics**

The Triangle struct cleanly encapsulates position and brightness parameters needed for triangle rendering. The structure follows good WGSL practices and aligns with the broader refactoring approach where type definitions are consolidated into main.wgsl files.

---

`10-12`: **Good utility function implementation**

The triangle_brightness function properly transforms the brightness parameter using a sine-based calculation to create a normalized brightness value between 0.5 and 1.0. This helper function keeps the code modular and reusable.

</details>
<details>
<summary>examples/triangles/vertices/main.wgsl (1)</summary>

`2-5`: **Clear Vertex struct definition**

The Vertex struct with its position field properly centralizes the vertex type definition in this main.wgsl file, which aligns with the refactoring pattern applied across the project. This structure correctly replaces the previously deleted definition from vertices/types.wgsl.

</details>
<details>
<summary>examples/triangles/background/main.wgsl (1)</summary>

`3-6`: **Clean Background struct definition**

The Background struct with its color field follows the consistent refactoring pattern of moving type definitions from separate type files into main.wgsl files. This structure provides a clear interface for background color handling.

</details>
<details>
<summary>examples/triangles/vertices/init.wgsl (1)</summary>

`3-3`: **Successful implementation of relative import path**

The import statement now uses the new `~.` syntax for relative paths, correctly referencing the storages module. This change aligns with the PR's primary objective of supporting relative imports in WGSL shaders.

</details>
<details>
<summary>examples/triangles/triangles/init.wgsl (1)</summary>

`3-3`: **Relative import syntax usage is correct.**

The updated directive `#import ~.storages` properly replaces the previous module path and aligns with the new `~.` parent-folder semantics introduced in the PR.

</details>
<details>
<summary>crates/wgso/tests/cases_valid/imports/.expected (1)</summary>

`11-13`: **Updated expected output matches test definitions.**

The entries  
```text
relative1=[2, 0, 0, 0]
relative2=[3, 0, 0, 0]
```  
correctly reflect the imported constants and adhere to existing formatting conventions.

</details>
<details>
<summary>examples/triangles/background/update.wgsl (1)</summary>

`3-3`: **Updated import to use parent-folder syntax.**

The `#import ~.storages` directive correctly adopts the new `~.` syntax and remains consistent with other examples.

</details>
<details>
<summary>examples/triangles/triangles/draw.wgsl (1)</summary>

`3-4`: **Import paths updated to use new relative syntax.**

The imports have been updated to use the new relative import syntax with the `~` symbol representing the parent directory. This change is part of the broader module reorganization where types from separate files were consolidated into main.wgsl files.

</details>
<details>
<summary>examples/triangles/background/draw.wgsl (1)</summary>

`3-4`: **Import paths updated to use new relative syntax.**

The imports have been updated to use the new relative import syntax with the `~` symbol representing the parent directory. This change properly reflects the reorganized module structure where type definitions were moved from separate files into main.wgsl files.

</details>
<details>
<summary>examples/triangles/triangles/update.wgsl (1)</summary>

`3-3`: **Import path updated to use new relative syntax.**

The import has been updated from `triangles.storages` to `~.storages` using the new tilde syntax for relative paths. This aligns with the overall import system changes.

</details>
<details>
<summary>examples/triangles/background/storages.wgsl (2)</summary>

`1-1`: **Import path updated to use new relative syntax.**

The import has been updated from `background.types` to `~.main` using the new tilde syntax for relative paths, correctly reflecting the module reorganization.

---

`5-7`: **Added explicit BackgroundState structure.**

The BackgroundState struct has been explicitly defined here with a single field referencing the Background type (which is now defined in main.wgsl). This change properly supports the module restructuring where type declarations were moved from .types.wgsl files to .main.wgsl files.

</details>
<details>
<summary>crates/wgso/tests/cases_valid/imports/relative/inner/relative.wgsl (2)</summary>

`4-5`: **Excellent test coverage for relative imports**

This test effectively validates both shallow relative imports (`~.side`) and deep multi-level imports (`~.~.~.root`), ensuring the new syntax works correctly at different directory depths.

---

`7-15`: **Well-designed test structure**

The test has a clean structure that effectively validates the import functionality by:
1. Defining storage variables that can be inspected in test results
2. Assigning imported constants to these variables
3. Creating a simple compute shader that exercises the import system

This approach makes it easy to automatically verify that the constants are correctly imported from their respective modules.

</details>
<details>
<summary>crates/wgso/src/program/module.rs (1)</summary>

`178-178`: **Update to path resolution logic looks good**

The change to pass `root_path` directly to `directive.import_path()` makes sense as it centralizes the path resolution logic within the method itself. This supports the new relative import syntax with `~.` and follows good design principles by delegating path resolution responsibility to the appropriate method.

</details>
<details>
<summary>examples/triangles/triangles/storages.wgsl (2)</summary>

`1-1`: **Relative import syntax looks good**

The switch from `#import triangles.types` to `#import ~.main` successfully demonstrates the new relative import syntax. This change aligns with the PR objective of adding imports with relative paths.

---

`3-9`: **Good refactoring of the storage structure**

Adding the `TRIANGLE_COUNT` constant and explicit `TriangleState` struct with an array of triangles improves code clarity and maintainability. This restructuring makes the relationship between the storage and its contents more explicit.

</details>
<details>
<summary>examples/triangles/vertices/storages.wgsl (2)</summary>

`1-1`: **Relative import syntax looks good**

The switch from `#import vertices.types` to `#import ~.main` successfully implements the new relative import syntax using the tilde notation.

---

`5-8`: **Good explicit vertex state structure**

Adding the explicit `VertexState` struct with separate arrays for triangle and rectangle vertices provides better structure and self-documentation. This makes the code more readable and maintainable by clearly separating different vertex collections.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-04T09:36:53Z
user: login: Nicolas-Ferre
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-04T09:39:35Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-04T09:40:01Z
user: login: Nicolas-Ferre
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-04T09:40:46Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-04T09:41:15Z

Review comments:
diff_hunk: @@ -142,6 +145,12 @@ draw: &draw
   - !token ')'
 
 import: &import
+  - !repeat
+    group:
+      - !pattern
+        label: import_segment
+        config: *parent_folder
+      - !token '.'
path: crates/wgso/config/directives.yaml
user: login: coderabbitai[bot]
body: _💡 Verification agent_

_🛠️ Refactor suggestion_

<details>
<summary>❓ Verification inconclusive</summary>

**Guard against ambiguous `~.` repetitions**  

The new leading repeat allows any number of `~.` pairs (including zero). That works, but a stray dot after the last `~` (e.g. `#import ~.`) will now parse and reach the resolver, which will later fail with a less helpful “file … does not exist” error.  
Two quick wins:  
1. Add `min: 1` to the `!repeat` block so the syntax `~.` is required for the *first* segment when the directive starts with `~`.  
2. Extend the parser tests with a negative case to ensure `#import ~.` is rejected at parse time.

---



</details>

**Require at least one `~.` repetition in import directives**  

The current `!repeat` block defaults to zero occurrences, so a stray `#import ~.` will parse and defer to the resolver, producing an unclear “file … does not exist” error. To guard against this:

• In crates/wgso/config/directives.yaml (lines 148–153), add `min: 1` to the `!repeat` mapping:  
```diff
-  - !repeat
+  - !repeat
+      min: 1
       group:
         - !pattern
           label: import_segment
           config: *parent_folder
         - !token '.'
```  
• Add a negative parser test (e.g. verify that `#import ~.` is rejected at parse time) to your existing import-directive tests.  
 

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  - !repeat
    min: 1
    group:
      - !pattern
        label: import_segment
        config: *parent_folder
      - !token '.'
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-04T09:36:52Z
author_association: NONE
diff_hunk: @@ -5,18 +5,30 @@ use std::path::{Path, PathBuf};
 use wgso_parser::ParsingError;
 
 impl Directive {
-    pub(crate) fn import_path(&self) -> PathBuf {
+    pub(crate) fn import_path(&self, root_path: &Path) -> PathBuf {
         let segment_count = self.find_all_by_label("import_segment").count();
+        let is_relative = self
+            .find_all_by_label("import_segment")
+            .next()
+            .is_some_and(|segment| segment.slice == "~");
+        let root_path = if is_relative {
+            let file_path = self.path();
+            file_path.parent().unwrap_or(file_path).to_path_buf()
+        } else {
+            root_path.to_path_buf()
+        };
path: crates/wgso/src/directives/imports.rs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Potential directory-traversal outside project root**  

With the new `~` syntax a shader could write something like `#import ~.~.~.~.secret`.  
Nothing prevents the fold below from walking above `root_path`, so the compiler may read files outside the workspace—undesirable in CI and untrusted source scenarios.

```diff
-        let root_path = if is_relative {
-            let file_path = self.path();
-            file_path.parent().unwrap_or(file_path).to_path_buf()
-        } else {
-            root_path.to_path_buf()
-        };
+        let mut root_path = if is_relative {
+            self.path()
+                .parent()
+                .unwrap_or_else(|| self.path())
+                .to_path_buf()
+        } else {
+            root_path.to_path_buf()
+        };
+
+        // Hard stop: never allow the resolved path to escape the declared project root.
+        let project_root = root_path.canonicalize().unwrap_or(root_path.clone());
+        if let Ok(candidate) = root_path.canonicalize() {
+            if !candidate.starts_with(&project_root) {
+                root_path = project_root;
+            }
+        }
```

This keeps the relative import feature while sandboxing it under the configured root.  
 


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
-        let root_path = if is_relative {
-            let file_path = self.path();
-            file_path.parent().unwrap_or(file_path).to_path_buf()
-        } else {
-            root_path.to_path_buf()
-        };
+        let mut root_path = if is_relative {
+            self.path()
+                .parent()
+                .unwrap_or_else(|| self.path())
+                .to_path_buf()
+        } else {
+            root_path.to_path_buf()
+        };
+
+        // Hard stop: never allow the resolved path to escape the declared project root.
+        let project_root = root_path
+            .canonicalize()
+            .unwrap_or(root_path.clone());
+        if let Ok(candidate) = root_path.canonicalize() {
+            if !candidate.starts_with(&project_root) {
+                root_path = project_root;
+            }
+        }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-04T09:36:52Z
author_association: NONE
diff_hunk: @@ -142,6 +145,12 @@ draw: &draw
   - !token ')'
 
 import: &import
+  - !repeat
+    group:
+      - !pattern
+        label: import_segment
+        config: *parent_folder
+      - !token '.'
path: crates/wgso/config/directives.yaml
user: login: Nicolas-Ferre
body: Min should actually be 0. If there is 0 occurrences, it just means that there is no "~", so for example "#import my.module". "#import ~." cannot occur.
created_at: 2025-05-04T09:39:35Z
author_association: OWNER
diff_hunk: @@ -142,6 +145,12 @@ draw: &draw
   - !token ')'
 
 import: &import
+  - !repeat
+    group:
+      - !pattern
+        label: import_segment
+        config: *parent_folder
+      - !token '.'
path: crates/wgso/config/directives.yaml
user: login: coderabbitai[bot]
body: @Nicolas-Ferre You're absolutely right! Setting `min: 0` is the correct approach here. This makes the parent folder reference optional, allowing both relative imports with the tilde pattern (`#import ~.module`) and regular imports (`#import my.module`).

I see now why `#import ~.` can't occur - the directive structure requires an identifier segment after any `~.` sequences. The parser would reject a bare `~.` because it's expecting an identifier to follow.

Thanks for the clarification!

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Nicolas-Ferre
PR: Nicolas-Ferre/wgso#49
File: crates/wgso/config/directives.yaml:148-153
Timestamp: 2025-05-04T09:40:00.352Z
Learning: The import directive in WGSO should have min: 0 for the parent folder pattern repeat block to make it optional, allowing both relative imports with "~." prefix and regular imports without it. The directive structure prevents a bare "#import ~." because it requires an identifier segment after any "~." sequences.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Nicolas-Ferre You're absolutely right! Setting `min: 0` is the correct approach here. This makes the parent folder reference optional, allowing both relative imports with the tilde pattern (`#import ~.module`) and regular imports (`#import my.module`).

I see now why `#import ~.` can't occur - the directive structure requires an identifier segment after any `~.` sequences. The parser would reject a bare `~.` because it's expecting an identifier to follow.

Thanks for the clarification!

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Nicolas-Ferre
PR: Nicolas-Ferre/wgso#49
File: crates/wgso/config/directives.yaml:148-153
Timestamp: 2025-05-04T09:40:00.352Z
Learning: The import directive in WGSO should have min: 0 for the parent folder pattern repeat block to make it optional, allowing both relative imports with "~." prefix and regular imports without it. The directive structure prevents a bare "#import ~." because it requires an identifier segment after any "~." sequences.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-04T09:40:01Z
author_association: NONE
diff_hunk: @@ -5,18 +5,30 @@ use std::path::{Path, PathBuf};
 use wgso_parser::ParsingError;
 
 impl Directive {
-    pub(crate) fn import_path(&self) -> PathBuf {
+    pub(crate) fn import_path(&self, root_path: &Path) -> PathBuf {
         let segment_count = self.find_all_by_label("import_segment").count();
+        let is_relative = self
+            .find_all_by_label("import_segment")
+            .next()
+            .is_some_and(|segment| segment.slice == "~");
+        let root_path = if is_relative {
+            let file_path = self.path();
+            file_path.parent().unwrap_or(file_path).to_path_buf()
+        } else {
+            root_path.to_path_buf()
+        };
path: crates/wgso/src/directives/imports.rs
user: login: Nicolas-Ferre
body: Actually, an import pointing on an outside file will fail, because only files inside project folder are loaded in memory
created_at: 2025-05-04T09:40:46Z
author_association: OWNER
diff_hunk: @@ -5,18 +5,30 @@ use std::path::{Path, PathBuf};
 use wgso_parser::ParsingError;
 
 impl Directive {
-    pub(crate) fn import_path(&self) -> PathBuf {
+    pub(crate) fn import_path(&self, root_path: &Path) -> PathBuf {
         let segment_count = self.find_all_by_label("import_segment").count();
+        let is_relative = self
+            .find_all_by_label("import_segment")
+            .next()
+            .is_some_and(|segment| segment.slice == "~");
+        let root_path = if is_relative {
+            let file_path = self.path();
+            file_path.parent().unwrap_or(file_path).to_path_buf()
+        } else {
+            root_path.to_path_buf()
+        };
path: crates/wgso/src/directives/imports.rs
user: login: coderabbitai[bot]
body: @Nicolas-Ferre Thank you for the clarification. I understand now that there's already a logical safeguard in place - since only files inside the project folder are loaded into memory (the `Files` collection), any import attempt that tries to reference a file outside that boundary would fail at the `files.exists(&path)` check in the `check` function. This means even if someone uses the `~` syntax to traverse directories, they can't access files outside the project scope. That's a good security design approach.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Nicolas-Ferre
PR: Nicolas-Ferre/wgso#49
File: crates/wgso/src/directives/imports.rs:14-19
Timestamp: 2025-05-04T09:41:13.697Z
Learning: In the WGSO codebase, imports using the relative path syntax with `~` cannot access files outside the project folder, as the `Files` collection only contains files inside the project boundary, and the `check` function verifies imports against this collection.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@Nicolas-Ferre Thank you for the clarification. I understand now that there's already a logical safeguard in place - since only files inside the project folder are loaded into memory (the `Files` collection), any import attempt that tries to reference a file outside that boundary would fail at the `files.exists(&path)` check in the `check` function. This means even if someone uses the `~` syntax to traverse directories, they can't access files outside the project scope. That's a good security design approach.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: Nicolas-Ferre
PR: Nicolas-Ferre/wgso#49
File: crates/wgso/src/directives/imports.rs:14-19
Timestamp: 2025-05-04T09:41:13.697Z
Learning: In the WGSO codebase, imports using the relative path syntax with `~` cannot access files outside the project folder, as the `Files` collection only contains files inside the project boundary, and the `check` function verifies imports against this collection.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-04T09:41:15Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-04T09:29:29Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This change introduces support for relative imports in WGSL shaders using the `~.` syntax in import directives. The configuration for directive parsing is updated to recognize the tilde (`~`) as a parent folder token, and the import directive grammar is extended to allow any number of `~.` segments at the start of an import path. The import path resolution logic in Rust is refactored to interpret these segments by traversing up the directory hierarchy accordingly. New and updated test cases are added to validate relative import behavior, and example shaders are refactored to utilize the new import syntax, consolidating type and storage definitions.

## Changes

| Files/Paths                                                                                          | Change Summary                                                                                                                                                                                                                                                                                                                                                                 |
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| crates/wgso/config/directives.yaml                                                                   | Added `parent_folder` pattern for `~`, updated import directive grammar to allow repeated `~.` at the start, moved `ident` pattern position.                                                                                                                                                                                                                                  |
| crates/wgso/src/directives/imports.rs                                                                | Refactored `import_path` to support relative imports by processing leading `~` segments, updated method signature to accept `root_path`, and revised path resolution logic.                                                                                                                     |
| crates/wgso/src/program/module.rs                                                                    | Updated to use the new `import_path(root_path)` signature, delegating path joining and resolution to the method.                                                                                                                                                                                                                       |
| crates/wgso/tests/cases_valid/imports/.expected                                                      | Added expected output arrays for new relative import test cases.                                                                                                                                                                                                                                                                         |
| crates/wgso/tests/cases_valid/imports/relative/inner/relative.wgsl<br>.../side.wgsl<br>../root.wgsl  | Added new WGSL shader test files to validate relative import behavior and provide imported constants.                                                                                                                                                                                                                                   |
| examples/triangles/background/draw.wgsl<br>.../storages.wgsl<br>.../update.wgsl                      | Updated import directives to use the new relative import syntax (`~.main`, `~.storages`), removed dependency on deleted types file, and updated struct usage.                                                                                                                                    |
| examples/triangles/background/main.wgsl                                                              | Added new struct `Background`.                                                                                                                                                                                                                                                                                                          |
| examples/triangles/background/types.wgsl                                                             | Deleted file; types are now defined in `main.wgsl` and `storages.wgsl`.                                                                                                                                                                                                                                                               |
| examples/triangles/triangles/draw.wgsl<br>.../init.wgsl<br>.../update.wgsl                           | Updated import directives to use new relative syntax, removed dependency on deleted types file.                                                                                                                                                                                                                                         |
| examples/triangles/triangles/main.wgsl                                                               | Added struct `Triangle` and function `triangle_brightness`.                                                                                                                                                                                                                                                                            |
| examples/triangles/triangles/storages.wgsl                                                           | Added constant `TRIANGLE_COUNT`, struct `TriangleState`, and updated storage variable type.                                                                                                                                                                                                                                            |
| examples/triangles/triangles/types.wgsl                                                              | Deleted file; types and constants are now defined in `main.wgsl` and `storages.wgsl`.                                                                                                                                                                                                           |
| examples/triangles/vertices/init.wgsl                                                                | Updated import directive to use new relative syntax.                                                                                                                                                                                                                                             |
| examples/triangles/vertices/main.wgsl                                                                | Added struct `Vertex`.                                                                                                                                                                                                                                                                                                                 |
| examples/triangles/vertices/storages.wgsl                                                            | Added struct `VertexState`, updated storage variable type, and changed import directive.                                                                                                                                                                                                        |
| examples/triangles/vertices/types.wgsl                                                               | Deleted file; types are now defined in `main.wgsl` and `storages.wgsl`.                                                                                                                                                                                                                        |

## Assessment against linked issues

| Objective (issue #)                                           | Addressed | Explanation                                                                                                 |
|---------------------------------------------------------------|-----------|-------------------------------------------------------------------------------------------------------------|
| Support relative paths in `#import` directives using `~.` syntax, allowing any number of `~` ( #41 ) | ✅        |                                                                                                             |

## Possibly related PRs

- Nicolas-Ferre/wgso#38: Introduced the initial import directive parsing and basic import path resolution, which is directly extended and modified by the current PR to support relative imports.

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Nicolas-Ferre/wgso&utm_content=49):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: codecov-commenter
created_at: 2025-05-04T09:32:16Z
body: ## [Codecov](https://app.codecov.io/gh/Nicolas-Ferre/wgso/pull/49?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+Ferr%C3%A9) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`fac5de8`)](https://app.codecov.io/gh/Nicolas-Ferre/wgso/commit/fac5de8d7a9692d281c6ad4c37f86f6010ba6628?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+Ferr%C3%A9) to head [(`f949ff3`)](https://app.codecov.io/gh/Nicolas-Ferre/wgso/commit/f949ff3eb626adcdfb41f103bb0f668df4ab93b5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+Ferr%C3%A9).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##              main       #49   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           16        16           
  Lines         1760      1770   +10     
=========================================
+ Hits          1760      1770   +10     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Nicolas-Ferre/wgso/pull/49?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+Ferr%C3%A9).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+Ferr%C3%A9).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE