all: import individual symbols feature (#5872)
parent
b3011b4f19
commit
1114fd28d0
53
doc/docs.md
53
doc/docs.md
|
@ -29,7 +29,7 @@ you can do in V.
|
||||||
* [Numbers](#numbers)
|
* [Numbers](#numbers)
|
||||||
* [Arrays](#arrays)
|
* [Arrays](#arrays)
|
||||||
* [Maps](#maps)
|
* [Maps](#maps)
|
||||||
* [Imports](#imports)
|
* [Module Imports](#module-imports)
|
||||||
* [Statements & Expressions](#statements--expressions)
|
* [Statements & Expressions](#statements--expressions)
|
||||||
* [If](#if)
|
* [If](#if)
|
||||||
* [In Operator](#in-operator)
|
* [In Operator](#in-operator)
|
||||||
|
@ -547,7 +547,13 @@ numbers := {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Imports
|
## Module Imports
|
||||||
|
|
||||||
|
For information about creating a module, see [Modules](#modules)
|
||||||
|
|
||||||
|
### Importing a Module
|
||||||
|
|
||||||
|
Modules can be imported using keyword `import`.
|
||||||
|
|
||||||
```v
|
```v
|
||||||
import os
|
import os
|
||||||
|
@ -558,7 +564,48 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Modules can be imported using keyword `import`. When using types, functions, and constants from other modules, the full path must be specified. In the example above, `name := input()` wouldn't work. That means that it's always clear from which module a function is called.
|
When using constants from other modules, the module name must be prefixed. However,
|
||||||
|
you can import functions and types from other modules directly:
|
||||||
|
|
||||||
|
```v
|
||||||
|
import os { input }
|
||||||
|
import crypto.sha256 { sum }
|
||||||
|
import time { Time }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Module Import Aliasing
|
||||||
|
|
||||||
|
Any imported module name can be aliased using the `as` keyword:
|
||||||
|
|
||||||
|
NOTE: this example will not compile unless you have created `mymod/sha256.v`
|
||||||
|
```v
|
||||||
|
import crypto.sha256
|
||||||
|
import mymod.sha256 as mysha256
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
v_hash := sha256.sum('hi'.bytes()).hex()
|
||||||
|
my_hash := mysha256.sum('hi'.bytes()).hex()
|
||||||
|
assert my_hash == v_hash
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You cannot alias an imported function or type.
|
||||||
|
However, you _can_ redeclare a type.
|
||||||
|
|
||||||
|
```v
|
||||||
|
import time
|
||||||
|
|
||||||
|
type MyTime time.Time
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
my_time := MyTime{
|
||||||
|
year: 2020,
|
||||||
|
month: 12,
|
||||||
|
day: 25
|
||||||
|
}
|
||||||
|
println(my_time.unix_time())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Statements & Expressions
|
## Statements & Expressions
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,20 @@ pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
mod string
|
mod string
|
||||||
alias string
|
alias string
|
||||||
|
pub mut:
|
||||||
|
syms []ImportSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ImportSymbolKind {
|
||||||
|
fn_
|
||||||
|
type_
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ImportSymbol {
|
||||||
|
pub:
|
||||||
|
pos token.Position
|
||||||
|
name string
|
||||||
|
kind ImportSymbolKind
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AnonFn {
|
pub struct AnonFn {
|
||||||
|
|
|
@ -382,6 +382,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
if type_sym.kind == .alias {
|
if type_sym.kind == .alias {
|
||||||
info_t := type_sym.info as table.Alias
|
info_t := type_sym.info as table.Alias
|
||||||
sym := c.table.get_type_symbol(info_t.parent_type)
|
sym := c.table.get_type_symbol(info_t.parent_type)
|
||||||
|
if sym.kind == .placeholder { // pending import symbol did not resolve
|
||||||
|
c.error('unknown struct: $type_sym.name', struct_init.pos)
|
||||||
|
return table.void_type
|
||||||
|
}
|
||||||
if sym.kind != .struct_ {
|
if sym.kind != .struct_ {
|
||||||
c.error('alias type name: $sym.name is not struct type', struct_init.pos)
|
c.error('alias type name: $sym.name is not struct type', struct_init.pos)
|
||||||
}
|
}
|
||||||
|
@ -2055,7 +2059,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
ast.GotoLabel {}
|
ast.GotoLabel {}
|
||||||
ast.GotoStmt {}
|
ast.GotoStmt {}
|
||||||
ast.HashStmt {}
|
ast.HashStmt {}
|
||||||
ast.Import {}
|
ast.Import {
|
||||||
|
c.import_stmt(node)
|
||||||
|
}
|
||||||
ast.InterfaceDecl {
|
ast.InterfaceDecl {
|
||||||
c.interface_decl(node)
|
c.interface_decl(node)
|
||||||
}
|
}
|
||||||
|
@ -2087,6 +2093,26 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) import_stmt(imp ast.Import) {
|
||||||
|
for sym in imp.syms {
|
||||||
|
name := '$imp.mod\.$sym.name'
|
||||||
|
if sym.kind == .fn_ {
|
||||||
|
c.table.find_fn(name) or {
|
||||||
|
c.error('module `$imp.mod` has no public fn named `$sym.name\()`', sym.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sym.kind == .type_ {
|
||||||
|
if type_sym := c.table.find_type(name) {
|
||||||
|
if type_sym.kind == .placeholder {
|
||||||
|
c.error('module `$imp.mod` has no public type `$sym.name\{}`', sym.pos)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.error('module `$imp.mod` has no public type `$sym.name\{}`', sym.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
||||||
mut unreachable := token.Position{
|
mut unreachable := token.Position{
|
||||||
line_nr: -1
|
line_nr: -1
|
||||||
|
@ -2919,7 +2945,6 @@ pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) table.Type {
|
||||||
typ_sym := c.table.get_type_symbol(typ)
|
typ_sym := c.table.get_type_symbol(typ)
|
||||||
// if !typ.is_number() {
|
// if !typ.is_number() {
|
||||||
if !typ_sym.is_number() {
|
if !typ_sym.is_number() {
|
||||||
println(typ_sym.kind.str())
|
|
||||||
c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)',
|
c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)',
|
||||||
node.pos)
|
node.pos)
|
||||||
} else {
|
} else {
|
||||||
|
@ -3232,7 +3257,6 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
mut idx := 0
|
mut idx := 0
|
||||||
for i, m in sym.methods {
|
for i, m in sym.methods {
|
||||||
if m.name == node.name {
|
if m.name == node.name {
|
||||||
println('got it')
|
|
||||||
idx = i
|
idx = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
vlib/v/checker/tests/import_not_same_line_err.v:2:2: error: `import` and `module` must be at same line
|
vlib/v/checker/tests/import_not_same_line_err.v:2:2: error: `import` statements must be a single line
|
||||||
1 | import
|
1 | import
|
||||||
2 | time
|
2 | time
|
||||||
| ~~~~
|
| ~~~~
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/checker/tests/import_symbol_empty.v:1:12: error: empty `os` import set, remove `{}`
|
||||||
|
1 | import os {}
|
||||||
|
| ^
|
|
@ -0,0 +1 @@
|
||||||
|
import os {}
|
|
@ -0,0 +1,11 @@
|
||||||
|
vlib/v/checker/tests/import_symbol_fn_err.v:1:17: error: module `crypto` has no public fn named `userper()`
|
||||||
|
1 | import crypto { userper }
|
||||||
|
| ~~~~~~~
|
||||||
|
2 | fn main() {
|
||||||
|
3 | usurper()
|
||||||
|
vlib/v/checker/tests/import_symbol_fn_err.v:3:3: error: unknown function: usurper
|
||||||
|
1 | import crypto { userper }
|
||||||
|
2 | fn main() {
|
||||||
|
3 | usurper()
|
||||||
|
| ~~~~~~~~~
|
||||||
|
4 | }
|
|
@ -0,0 +1,4 @@
|
||||||
|
import crypto { userper }
|
||||||
|
fn main() {
|
||||||
|
usurper()
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/checker/tests/import_symbol_invalid.v:1:17: error: import syntax error, please specify a valid fn or type name
|
||||||
|
1 | import crypto { *_v }
|
||||||
|
| ^
|
|
@ -0,0 +1 @@
|
||||||
|
import crypto { *_v }
|
|
@ -0,0 +1,11 @@
|
||||||
|
vlib/v/checker/tests/import_symbol_type_err.v:1:17: error: module `crypto` has no public type `Coin{}`
|
||||||
|
1 | import crypto { Coin }
|
||||||
|
| ~~~~
|
||||||
|
2 | fn main() {
|
||||||
|
3 | println(Coin{})
|
||||||
|
vlib/v/checker/tests/import_symbol_type_err.v:3:11: error: unknown struct: Coin
|
||||||
|
1 | import crypto { Coin }
|
||||||
|
2 | fn main() {
|
||||||
|
3 | println(Coin{})
|
||||||
|
| ~~~~~~
|
||||||
|
4 | }
|
|
@ -0,0 +1,4 @@
|
||||||
|
import crypto { Coin }
|
||||||
|
fn main() {
|
||||||
|
println(Coin{})
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
vlib/v/checker/tests/import_symbol_unclosed.v:1:28: error: import syntax error, no closing `}`
|
||||||
|
1 | import crypto.sha256 { sum ]
|
||||||
|
| ^
|
|
@ -0,0 +1 @@
|
||||||
|
import crypto.sha256 { sum ]
|
|
@ -1,4 +1,4 @@
|
||||||
vlib/v/checker/tests/import_syntax_err.v:1:12: error: module syntax error, please use `x.y.z`
|
vlib/v/checker/tests/import_syntax_err.v:1:12: error: cannot import multiple modules at a time
|
||||||
1 | import time, os
|
1 | import time, os
|
||||||
| ^
|
| ^
|
||||||
2 | fn main() {
|
2 | fn main() {
|
||||||
|
|
|
@ -77,6 +77,10 @@ pub fn fmt(file ast.File, table &table.Table, is_debug bool) string {
|
||||||
pub fn (mut f Fmt) process_file_imports(file &ast.File) {
|
pub fn (mut f Fmt) process_file_imports(file &ast.File) {
|
||||||
for imp in file.imports {
|
for imp in file.imports {
|
||||||
f.mod2alias[imp.mod.all_after_last('.')] = imp.alias
|
f.mod2alias[imp.mod.all_after_last('.')] = imp.alias
|
||||||
|
for sym in imp.syms {
|
||||||
|
f.mod2alias['$imp.mod\.$sym.name'] = sym.name
|
||||||
|
f.mod2alias[sym.name] = sym.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +232,10 @@ pub fn (mut f Fmt) imports(imports []ast.Import) {
|
||||||
|
|
||||||
pub fn (f Fmt) imp_stmt_str(imp ast.Import) string {
|
pub fn (f Fmt) imp_stmt_str(imp ast.Import) string {
|
||||||
is_diff := imp.alias != imp.mod && !imp.mod.ends_with('.' + imp.alias)
|
is_diff := imp.alias != imp.mod && !imp.mod.ends_with('.' + imp.alias)
|
||||||
imp_alias_suffix := if is_diff { ' as $imp.alias' } else { '' }
|
mut imp_alias_suffix := if is_diff { ' as $imp.alias' } else { '' }
|
||||||
|
if imp.syms.len > 0 {
|
||||||
|
imp_alias_suffix += ' { ' + imp.syms.map(it.name).join(', ') + ' }'
|
||||||
|
}
|
||||||
return '$imp.mod$imp_alias_suffix'
|
return '$imp.mod$imp_alias_suffix'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1338,8 +1345,11 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
|
||||||
f.or_expr(node.or_block)
|
f.or_expr(node.or_block)
|
||||||
} else {
|
} else {
|
||||||
f.write_language_prefix(node.language)
|
f.write_language_prefix(node.language)
|
||||||
name := f.short_module(node.name)
|
mut name := f.short_module(node.name)
|
||||||
f.mark_module_as_used(name)
|
f.mark_module_as_used(name)
|
||||||
|
if node.name in f.mod2alias {
|
||||||
|
name = f.mod2alias[node.name]
|
||||||
|
}
|
||||||
f.write('$name')
|
f.write('$name')
|
||||||
if node.generic_type != 0 && node.generic_type != table.void_type {
|
if node.generic_type != 0 && node.generic_type != table.void_type {
|
||||||
f.write('<')
|
f.write('<')
|
||||||
|
|
|
@ -10,7 +10,7 @@ import v.util
|
||||||
|
|
||||||
pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExpr {
|
pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExpr {
|
||||||
first_pos := p.tok.position()
|
first_pos := p.tok.position()
|
||||||
fn_name := if language == .c {
|
mut fn_name := if language == .c {
|
||||||
'C.$p.check_name()'
|
'C.$p.check_name()'
|
||||||
} else if language == .js {
|
} else if language == .js {
|
||||||
'JS.$p.check_js_name()'
|
'JS.$p.check_js_name()'
|
||||||
|
@ -81,10 +81,17 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
p.next()
|
p.next()
|
||||||
or_kind = .propagate
|
or_kind = .propagate
|
||||||
}
|
}
|
||||||
|
mut fn_mod := p.mod
|
||||||
|
if registered := p.table.find_fn(fn_name) {
|
||||||
|
if registered.is_placeholder {
|
||||||
|
fn_mod = registered.mod
|
||||||
|
fn_name = registered.name
|
||||||
|
}
|
||||||
|
}
|
||||||
node := ast.CallExpr{
|
node := ast.CallExpr{
|
||||||
name: fn_name
|
name: fn_name
|
||||||
args: args
|
args: args
|
||||||
mod: p.mod
|
mod: fn_mod
|
||||||
pos: pos
|
pos: pos
|
||||||
language: language
|
language: language
|
||||||
or_block: ast.OrExpr{
|
or_block: ast.OrExpr{
|
||||||
|
|
|
@ -1331,7 +1331,7 @@ fn (mut p Parser) import_stmt() ast.Import {
|
||||||
}
|
}
|
||||||
mut mod_name := p.check_name()
|
mut mod_name := p.check_name()
|
||||||
if import_pos.line_nr != pos.line_nr {
|
if import_pos.line_nr != pos.line_nr {
|
||||||
p.error_with_pos('`import` and `module` must be at same line', pos)
|
p.error_with_pos('`import` statements must be a single line', pos)
|
||||||
}
|
}
|
||||||
mut mod_alias := mod_name
|
mut mod_alias := mod_name
|
||||||
for p.tok.kind == .dot {
|
for p.tok.kind == .dot {
|
||||||
|
@ -1351,25 +1351,90 @@ fn (mut p Parser) import_stmt() ast.Import {
|
||||||
p.next()
|
p.next()
|
||||||
mod_alias = p.check_name()
|
mod_alias = p.check_name()
|
||||||
}
|
}
|
||||||
|
node := ast.Import{
|
||||||
|
pos: pos,
|
||||||
|
mod: mod_name,
|
||||||
|
alias: mod_alias,
|
||||||
|
}
|
||||||
|
if p.tok.kind == .lcbr { // import module { fn1, Type2 } syntax
|
||||||
|
p.import_syms(node)
|
||||||
|
p.register_used_import(mod_name) // no `unused import` msg for parent
|
||||||
|
}
|
||||||
pos_t := p.tok.position()
|
pos_t := p.tok.position()
|
||||||
if import_pos.line_nr == pos_t.line_nr {
|
if import_pos.line_nr == pos_t.line_nr {
|
||||||
if p.tok.kind != .name {
|
if p.tok.kind != .lcbr {
|
||||||
p.error_with_pos('module syntax error, please use `x.y.z`', pos_t)
|
|
||||||
} else {
|
|
||||||
p.error_with_pos('cannot import multiple modules at a time', pos_t)
|
p.error_with_pos('cannot import multiple modules at a time', pos_t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.imports[mod_alias] = mod_name
|
p.imports[mod_alias] = mod_name
|
||||||
p.table.imports << mod_name
|
p.table.imports << mod_name
|
||||||
node := ast.Import{
|
|
||||||
mod: mod_name
|
|
||||||
alias: mod_alias
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
p.ast_imports << node
|
p.ast_imports << node
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// import_syms parses the inner part of `import module { submod1, submod2 }`
|
||||||
|
fn (mut p Parser) import_syms(mut parent ast.Import) {
|
||||||
|
p.next()
|
||||||
|
pos_t := p.tok.position()
|
||||||
|
if p.tok.kind == .rcbr { // closed too early
|
||||||
|
p.error_with_pos('empty `$parent.mod` import set, remove `{}`', pos_t)
|
||||||
|
}
|
||||||
|
if p.tok.kind != .name { // not a valid inner name
|
||||||
|
p.error_with_pos('import syntax error, please specify a valid fn or type name', pos_t)
|
||||||
|
}
|
||||||
|
for p.tok.kind == .name {
|
||||||
|
pos := p.tok.position()
|
||||||
|
alias := p.check_name()
|
||||||
|
name := '$parent.mod\.$alias'
|
||||||
|
if alias[0].is_capital() {
|
||||||
|
idx := p.table.add_placeholder_type(name)
|
||||||
|
typ := table.new_type(idx)
|
||||||
|
p.table.register_type_symbol({
|
||||||
|
kind: .alias
|
||||||
|
name: p.prepend_mod(alias)
|
||||||
|
parent_idx: idx
|
||||||
|
mod: p.mod
|
||||||
|
info: table.Alias{
|
||||||
|
parent_type: typ
|
||||||
|
language: table.Language.v
|
||||||
|
}
|
||||||
|
is_public: false
|
||||||
|
})
|
||||||
|
// so we can work with the fully declared type in fmt+checker
|
||||||
|
parent.syms << ast.ImportSymbol{
|
||||||
|
pos: pos
|
||||||
|
name: alias
|
||||||
|
kind: .type_
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !p.table.known_fn(name) {
|
||||||
|
p.table.fns[alias] = table.Fn{
|
||||||
|
is_placeholder: true
|
||||||
|
mod: parent.mod
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// so we can work with this in fmt+checker
|
||||||
|
parent.syms << ast.ImportSymbol{
|
||||||
|
pos: pos
|
||||||
|
name: alias
|
||||||
|
kind: .fn_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.tok.kind == .comma { // go again if more than one
|
||||||
|
p.next()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.tok.kind == .rcbr { // finish if closing `}` is seen
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.tok.kind != .rcbr {
|
||||||
|
p.error_with_pos('import syntax error, no closing `}`', p.tok.position())
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) const_decl() ast.ConstDecl {
|
fn (mut p Parser) const_decl() ast.ConstDecl {
|
||||||
p.top_level_statement_start()
|
p.top_level_statement_start()
|
||||||
start_pos := p.tok.position()
|
start_pos := p.tok.position()
|
||||||
|
|
|
@ -22,19 +22,20 @@ pub mut:
|
||||||
|
|
||||||
pub struct Fn {
|
pub struct Fn {
|
||||||
pub:
|
pub:
|
||||||
args []Arg
|
args []Arg
|
||||||
return_type Type
|
return_type Type
|
||||||
is_variadic bool
|
is_variadic bool
|
||||||
language Language
|
language Language
|
||||||
is_generic bool
|
is_generic bool
|
||||||
is_pub bool
|
is_pub bool
|
||||||
is_deprecated bool
|
is_deprecated bool
|
||||||
is_unsafe bool
|
is_unsafe bool
|
||||||
mod string
|
is_placeholder bool
|
||||||
ctdefine string // compile time define. myflag, when [if myflag] tag
|
mod string
|
||||||
attrs []string
|
ctdefine string // compile time define. myflag, when [if myflag] tag
|
||||||
|
attrs []string
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Arg {
|
pub struct Arg {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import os
|
import os
|
||||||
import time as t
|
|
||||||
import crypto.sha256
|
import crypto.sha256
|
||||||
import math
|
import term { white }
|
||||||
|
import crypto.md5 { sum }
|
||||||
import log as l
|
import log as l
|
||||||
|
import time as t { now, utc, Time }
|
||||||
|
import math
|
||||||
import crypto.sha512
|
import crypto.sha512
|
||||||
|
|
||||||
struct TestAliasInStruct {
|
struct TestAliasInStruct {
|
||||||
|
@ -11,8 +13,15 @@ struct TestAliasInStruct {
|
||||||
|
|
||||||
fn test_import() {
|
fn test_import() {
|
||||||
info := l.Level.info
|
info := l.Level.info
|
||||||
assert os.o_rdonly == os.o_rdonly && t.month_days[0] == t.month_days[0] && sha256.size ==
|
assert info == .info
|
||||||
sha256.size && math.pi == math.pi && info == .info && sha512.size == sha512.size
|
assert term.white('INFO') == white('INFO')
|
||||||
|
assert os.o_rdonly == os.o_rdonly
|
||||||
|
assert t.month_days[0] == t.month_days[0]
|
||||||
|
assert sha256.size == sha256.size
|
||||||
|
assert math.pi == math.pi
|
||||||
|
assert sha512.size == sha512.size
|
||||||
|
assert md5.sum('module'.bytes()).hex() == sum('module'.bytes()).hex()
|
||||||
|
assert t.utc().unix_time() == utc().unix_time()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_alias_in_struct_field() {
|
fn test_alias_in_struct_field() {
|
||||||
|
|
Loading…
Reference in New Issue