• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moonbitlang / x / 867

18 Jun 2026 06:38AM UTC coverage: 88.523% (+1.7%) from 86.827%
867

push

github

myfreess
path: document Node-compatible dispatch

2553 of 2884 relevant lines covered (88.52%)

338.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.72
/path/path.mbt
1
// Copyright 2025 International Digital Economy Academy
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
///|
16
/// Path utilities with the existing MoonBit API surface.
17
///
18
/// Behavior follows Node `node:path` for the active platform flavor. The
19
/// top-level package dispatches to `@path/posix` or `@path/win32` at runtime.
20

21
///|
22
let is_windows : Bool = @ffi.is_windows()
1✔
23

24
///|
25
/// A newtype wrapper provide path operation methods.
26
pub(all) struct Path(String) derive(Eq, Debug)
27

28
///|
29
pub impl Show for Path with fn output(path, logger) {
30
  logger.write_object(path.0)
×
31
}
32

33
///|
34
pub impl Show for Path with fn to_string(path) {
35
  path.0
3✔
36
}
37

38
///|
39
/// Returns the last path component of the given path.
40
///
41
/// Trailing separators are ignored, following Node `path.basename`.
42
/// 
43
/// ```mbt check
44
/// test {
45
///   let path : Path = "usr/local/bin"
46
///   inspect(path.basename(), content="bin")
47
/// }
48
/// ```
49
pub fn Path::basename(path : Path) -> StringView {
50
  if is_windows {
1✔
51
    @win32.Path::basename(path.0)
×
52
  } else {
53
    @posix.Path::basename(path.0)
1✔
54
  }
55
}
56

57
///|
58
/// Returns the directory portion of the path, following Node `path.dirname`.
59
/// 
60
/// ```mbt check
61
/// test {
62
///   let path : Path = "usr/local/bin"
63
///   inspect(path.dirname(), content="usr/local")
64
/// }
65
/// ```
66
pub fn Path::dirname(path : Path) -> Path {
67
  if is_windows {
1✔
68
    @win32.Path::dirname(path.0).0
×
69
  } else {
70
    @posix.Path::dirname(path.0).0
1✔
71
  }
72
}
73

74
///|
75
/// Returns the extension of the last path component, following Node
76
/// `path.extname` dotfile rules.
77
/// 
78
/// ```mbt check
79
/// test {
80
///   let path : Path = "archive.tar.gz"
81
///   inspect(path.extname(), content=".gz")
82
/// }
83
/// ```
84
pub fn Path::extname(path : Path) -> StringView {
85
  if is_windows {
2✔
86
    @win32.Path::extname(path.0)
×
87
  } else {
88
    @posix.Path::extname(path.0)
2✔
89
  }
90
}
91

92
///|
93
/// Returns whether the given path is absolute.
94
/// 
95
/// edge cases: when path is empty, return false
96
/// 
97
/// ```mbt check
98
/// test {
99
///   let path1 : Path = "/usr/local/bin"
100
///   let path2 : Path = "usr/local/bin"
101
///   json_inspect(path1.is_absolute(), content=true)
102
///   json_inspect(path2.is_absolute(), content=false)
103
/// }
104
/// ```
105
pub fn Path::is_absolute(path : Path) -> Bool {
106
  if is_windows {
2✔
107
    @win32.Path::is_absolute(path.0)
×
108
  } else {
109
    @posix.Path::is_absolute(path.0)
2✔
110
  }
111
}
112

113
///|
114
/// Concatenates two path strings with the active separator, then normalizes.
115
///
116
/// Absolute right-hand paths do not override `lhs`. When both inputs are empty,
117
/// the result is `"."`.
118
/// 
119
pub fn Path::join(lhs : Path, rhs : Path) -> Path {
120
  if is_windows {
1✔
121
    @win32.Path::join(lhs.0, rhs.0).0
×
122
  } else {
123
    @posix.Path::join(lhs.0, rhs.0).to_string()
1✔
124
  }
125
}
126

127
///|
128
/// 1. resolve `.` by directly removing it
129
/// 2. resolve `..` by removing the preceding path component if exists, otherwise keep it.
130
/// 3. remove redundant slashes or backslashes
131
/// 4. preserve trailing slash or backslash
132
/// 
133
/// edge cases:
134
///   1. when path is empty, return `.`
135
///   2. POSIX leading `//` is normalized to `/`, following Node behavior.
136
/// 
137
/// See `@path/posix` and `@path/win32` for separator-specific examples.
138
pub fn Path::normalize(path : Path) -> Path {
139
  if is_windows {
1✔
140
    @win32.Path::normalize(path.0).to_string()
×
141
  } else {
142
    @posix.Path::normalize(path.0).to_string()
1✔
143
  }
144
}
145

146
///|
147
/// Return the `to` relative path when `from` is the current working directory.
148
/// 
149
/// property: `@path.join(from,@path.relative(from,to)) == @path.normalize(to)`
150
/// 
151
/// edge cases: 
152
///  1. when `@path.normalize(from) == @path.normalize(to)`, return empty string
153
///  2. on Windows, different roots return the resolved target path
154
/// 
155
/// Warning: cwd is already resolve symbolic link and normalized, but path doesn't.
156
/// 
157
/// See `@path/posix` and `@path/win32` for separator-specific examples.
158
pub fn Path::relative(path : Path, base~ : Path) -> Path {
159
  if is_windows {
×
160
    @win32.Path::relative(path.0, base=base.0).0
×
161
  } else {
162
    @posix.Path::relative(path.0, base=base.0).0
×
163
  }
164
}
165

166
///|
167
/// 1. if path is already absolute, return path normalized.
168
/// 2. if path is relative, join current working directory and path, and then normalize it.
169
///
170
/// Warning: cwd is already resolve symbolic link and normalized, but path doesn't.
171
/// 
172
pub fn Path::resolve(path : Path) -> Path {
173
  if is_windows {
×
174
    @win32.Path::resolve(path.0).to_string()
×
175
  } else {
176
    @posix.Path::resolve(path.0).to_string()
×
177
  }
178
}
179

180
///|
181
/// OS platform specific path delimiter for environment variables. e.g., PATH
182
pub let delimiter : Char = if is_windows { ';' } else { ':' }
×
183

184
///|
185
/// OS platform specific path component separator.
186
pub let sep : Char = if is_windows { '\\' } else { '/' }
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc