1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

Merge branch 'vlang:master' into sqlite3_exec_param

This commit is contained in:
jacksonmowry 2023-08-06 23:25:56 +00:00 committed by GitHub
commit 9e8d6aada5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 786 additions and 374 deletions

View File

@ -97,6 +97,7 @@ jobs:
mkdir -p ~/.vmodules
ln -s $(pwd) ~/.vmodules/ui
../v examples/rectangles.v
../v examples/users.v
## ../v run examples/build_examples.vsh
- name: V self compilation with -usecache
run: |

View File

@ -9,7 +9,7 @@ import semver
const (
tool_name = os.file_name(os.executable())
tool_version = '0.0.1'
tool_version = '0.1.0'
tool_description = '\n Bump the semantic version of the v.mod and/or specified files.
The first instance of a version number is replaced with the new version.
@ -21,6 +21,11 @@ const (
version: \'0.2.42\'
VERSION = "1.23.8"
If certain lines need to be skipped, use the --skip option. For instance,
the following command will skip lines containing "tool-version":
v bump --patch --skip "tool-version" [files...]
Examples:
Bump the patch version in v.mod if it exists
v bump --patch
@ -37,6 +42,7 @@ struct Options {
major bool
minor bool
patch bool
skip string
}
type ReplacementFunction = fn (re regex.RE, input string, start int, end int) string
@ -67,10 +73,10 @@ fn get_replacement_function(options Options) ReplacementFunction {
return replace_with_increased_patch_version
}
fn process_file(input_file string, options Options) {
lines := os.read_lines(input_file) or { panic('Failed to read file: ${input_file}') }
fn process_file(input_file string, options Options) ! {
lines := os.read_lines(input_file) or { return error('Failed to read file: ${input_file}') }
mut re := regex.regex_opt(semver_query) or { panic('Could not create a RegEx parser.') }
mut re := regex.regex_opt(semver_query) or { return error('Could not create a RegEx parser.') }
repl_fn := get_replacement_function(options)
@ -85,7 +91,8 @@ fn process_file(input_file string, options Options) {
}
// Check if replacement is necessary
updated_line := if line.to_lower().contains('version') {
updated_line := if line.to_lower().contains('version') && !(options.skip != ''
&& line.contains(options.skip)) {
replacement_complete = true
re.replace_by_fn(line, repl_fn)
} else {
@ -103,11 +110,11 @@ fn process_file(input_file string, options Options) {
os.rm(backup_file) or {}
// Rename the original to the backup.
os.mv(input_file, backup_file) or { panic('Failed to copy file: ${input_file}') }
os.mv(input_file, backup_file) or { return error('Failed to copy file: ${input_file}') }
// Process the old file and write it back to the original.
os.write_file(input_file, new_lines.join_lines()) or {
panic('Failed to write file: ${input_file}')
return error('Failed to write file: ${input_file}')
}
// Remove the backup file.
@ -122,7 +129,7 @@ fn process_file(input_file string, options Options) {
fn main() {
if os.args.len < 2 {
println('Usage: ${tool_name} [options] [file1 file2 ...]
eprintln('Usage: ${tool_name} [options] [file1 file2 ...]
${tool_description}
Try ${tool_name} -h for more help...')
exit(1)
@ -141,6 +148,12 @@ Try ${tool_name} -h for more help...')
patch: fp.bool('patch', `p`, false, 'Bump the patch version.')
minor: fp.bool('minor', `n`, false, 'Bump the minor version.')
major: fp.bool('major', `m`, false, 'Bump the major version.')
skip: fp.string('skip', `s`, '', 'Skip lines matching this substring.').trim_space()
}
remaining := fp.finalize() or {
eprintln(fp.usage())
exit(1)
}
if options.show_help {
@ -148,24 +161,33 @@ Try ${tool_name} -h for more help...')
exit(0)
}
validate_options(options) or { panic(err) }
validate_options(options) or {
eprintln(fp.usage())
exit(1)
}
files := os.args[3..]
files := remaining[1..]
if files.len == 0 {
if !os.exists('v.mod') {
println('v.mod does not exist. You can create one using "v init".')
eprintln('v.mod does not exist. You can create one using "v init".')
exit(1)
}
process_file('v.mod', options) or {
eprintln('Failed to process v.mod: ${err}')
exit(1)
}
process_file('v.mod', options)
}
for input_file in files {
if !os.exists(input_file) {
println('File not found: ${input_file}')
eprintln('File not found: ${input_file}')
exit(1)
}
process_file(input_file, options) or {
eprintln('Failed to process ${input_file}: ${err}')
exit(1)
}
process_file(input_file, options)
}
}

View File

@ -78,7 +78,7 @@ fn run_individual_test(case BumpTestCase) ! {
os.rm(test_file) or {}
os.write_file(test_file, case.contents)!
//
os.execute_or_exit('${os.quoted_path(vexe)} bump --patch ${os.quoted_path(test_file)}')
patch_lines := os.read_lines(test_file)!
assert patch_lines[case.line] == case.expected_patch
@ -90,7 +90,7 @@ fn run_individual_test(case BumpTestCase) ! {
os.execute_or_exit('${os.quoted_path(vexe)} bump --major ${os.quoted_path(test_file)}')
major_lines := os.read_lines(test_file)!
assert major_lines[case.line] == case.expected_major
//
os.rm(test_file)!
}
@ -99,3 +99,65 @@ fn test_all_bump_cases() {
run_individual_test(case) or { panic(err) }
}
}
struct SkipTestCase {
file_name string
contents string
skip string
line int
expected_patch string
expected_minor string
expected_major string
}
const skip_test_cases = [
SkipTestCase{
file_name: 'CITATION.cff'
contents: 'abstract: A sample CLI tool made in V that prints geometric shapes to the screen.
authors:
- alias: hungrybluedev
family-names: Haldar
given-names: Subhomoy
cff-version: 1.2.0
date-released: 2023-04-20
license: MIT
message: Please cite this software using these information.
repository-code: https://github.com/hungrybluedev/geo
title: geo
url: https://github.com/hungrybluedev/geo
version: 0.2.4
'
line: 12
skip: 'cff-version'
expected_patch: 'version: 0.2.5'
expected_minor: 'version: 0.3.0'
expected_major: 'version: 1.0.0'
},
]
fn run_skip_test(case SkipTestCase) ! {
test_file := os.join_path_single(tfolder, case.file_name)
os.rm(test_file) or {}
os.write_file(test_file, case.contents)!
os.execute_or_exit('${os.quoted_path(vexe)} bump --patch --skip="${case.skip}" ${os.quoted_path(test_file)}')
patch_lines := os.read_lines(test_file)!
assert patch_lines[case.line] == case.expected_patch
os.execute_or_exit('${os.quoted_path(vexe)} bump --minor --skip="${case.skip}" ${os.quoted_path(test_file)}')
minor_lines := os.read_lines(test_file)!
assert minor_lines[case.line] == case.expected_minor
os.execute_or_exit('${os.quoted_path(vexe)} bump --major --skip="${case.skip}" ${os.quoted_path(test_file)}')
major_lines := os.read_lines(test_file)!
assert major_lines[case.line] == case.expected_major
os.rm(test_file)!
}
fn test_all_skip_bump_cases() ! {
for case in skip_test_cases {
run_skip_test(case) or { panic(err) }
}
}

View File

@ -74,12 +74,12 @@ fn on_frame(mut app App) {
// draw minute hand
mut j := f32(n.minute)
if n.second == 59 { // make minute hand move smoothly
j += f32(math.sin(f32(n.microsecond) / 1e6 * math.pi / 2.0))
j += f32(math.sin(f32(n.nanosecond) / 1e9 * math.pi / 2.0))
}
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.minute_hand, hand_color, j * 6)
// draw second hand with smooth transition
k := f32(n.second) + f32(math.sin(f32(n.microsecond) / 1e6 * math.pi / 2.0))
k := f32(n.second) + f32(math.sin(f32(n.nanosecond) / 1e9 * math.pi / 2.0))
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.second_hand, second_hand_color,
0 + k * 6)

View File

@ -0,0 +1,230 @@
module notify
import time
import os
#insert "@VEXEROOT/vlib/os/notify/kqueue.h"
struct C.kevent {
mut:
ident u32
filter i16
flags u16
fflags u32
data int
udata voidptr
}
fn C.kqueue() int
fn C.__kevent__(int, voidptr, int, voidptr, int, voidptr) int
fn C.EV_SET(voidptr, u32, i16, u16, u32, int, voidptr)
// KqueueNotifier provides methods that implement FdNotifier using the
// kqueue I/O event notification facility (macos, freeBSD, xxxBSD...unix only)
[noinit]
struct KqueueNotifier {
kqueue_fd int
}
// KqueueEvent describes an event that occurred for a file descriptor in
// the watch list
[noinit]
struct KqueueEvent {
pub:
fd int
kind FdEventType
}
// new creates a new KqueueNotifier
// The FdNotifier interface is returned to allow OS specific
// implementations without exposing the concrete type
pub fn new() !FdNotifier {
fd := C.kqueue()
if fd == -1 {
return error(os.posix_get_error_msg(C.errno))
}
// Needed to circumvent V limitations
x := &KqueueNotifier{
kqueue_fd: fd
}
return x
}
const (
// filter types
kqueue_read = i16(C.EVFILT_READ)
kqueue_write = i16(C.EVFILT_WRITE)
kqueue_aio = i16(C.EVFILT_AIO)
kqueue_vnode = i16(C.EVFILT_VNODE)
kqueue_proc = i16(C.EVFILT_PROC)
kqueue_signal = i16(C.EVFILT_SIGNAL)
kqueue_timer = i16(C.EVFILT_TIMER)
kqueue_machport = i16(C.EVFILT_MACHPORT)
kqueue_fs = i16(C.EVFILT_FS)
kqueue_user = i16(C.EVFILT_USER)
kqueue_vm = i16(C.EVFILT_VM)
kqueue_exception = i16(C.EVFILT_EXCEPT)
kqueue_syscount = i16(C.EVFILT_SYSCOUNT)
// actions
kqueue_add = u16(C.EV_ADD)
kqueue_delete = u16(C.EV_DELETE)
kqueue_enable = u16(C.EV_ENABLE)
kqueue_disable = u16(C.EV_DISABLE)
// flags
kqueue_oneshot = u16(C.EV_ONESHOT)
kqueue_edge_trigger = u16(C.EV_CLEAR) // kqueue_clear
kqueue_receipt = u16(C.EV_RECEIPT)
kqueue_dispatch = u16(C.EV_DISPATCH)
kqueue_udata_specific = u16(C.EV_UDATA_SPECIFIC)
kqueue_dispatch2 = u16(C.EV_DISPATCH | C.EV_UDATA_SPECIFIC)
kqueue_vanished = u16(C.EV_VANISHED)
kqueue_sysflags = u16(C.EV_SYSFLAGS)
kqueue_flag0 = u16(C.EV_FLAG0)
kqueue_flag1 = u16(C.EV_FLAG1)
// returned values
kqueue_eof = u16(C.EV_EOF)
kqueue_error = u16(C.EV_ERROR)
)
// ctl is a helper method for add, modify, and remove
fn (mut kn KqueueNotifier) ctl(fd int, filter i16, flags u16) ! {
event := [1]C.kevent{}
C.EV_SET(&event[0], fd, filter, flags, 0, 0, unsafe { nil })
if C.__kevent__(kn.kqueue_fd, &event[0], 1, unsafe { nil }, 0, unsafe { nil }) == -1 {
return error(os.posix_get_error_msg(C.errno))
}
}
// add adds a file descriptor to the watch list
fn (mut kn KqueueNotifier) add(fd int, events FdEventType, conf ...FdConfigFlags) ! {
filter := filter_to_mask(events)
flags := flags_to_mask(...conf)
kn.ctl(fd, filter, flags)!
}
// modify sets an existing entry in the watch list to the provided events and configuration
fn (mut kn KqueueNotifier) modify(fd int, events FdEventType, conf ...FdConfigFlags) ! {
kn.add(fd, events, ...conf)!
}
// remove removes a file descriptor from the watch list
fn (mut kn KqueueNotifier) remove(fd int) ! {
filter := notify.kqueue_read | notify.kqueue_write | notify.kqueue_exception
flags := notify.kqueue_delete
kn.ctl(fd, filter, flags)!
}
// wait waits to be notified of events on the watch list,
// returns at most 512 events
fn (mut kn KqueueNotifier) wait(timeout time.Duration) []FdEvent {
// arbitrary 512 limit; events will round robin on successive
// waits if the number exceeds this
// NOTE: we use a fixed size array here for stack allocation; this has
// the added bonus of making KqueueNotifier thread safe
events := [512]C.kevent{}
// populate events with the new events
to := &C.timespec{0, timeout.nanoseconds()}
count := C.__kevent__(kn.kqueue_fd, unsafe { nil }, 0, &events[0], events.len, to)
if count > 0 {
mut arr := []FdEvent{cap: count}
for i := 0; i < count; i++ {
fd := int(events[i].ident)
kind := event_mask_to_flag(events[i].filter, events[i].flags)
if kind.is_empty() {
// NOTE: tcc only reports the first event for some
// reason, leaving subsequent structs in the array as 0
// (or possibly garbage)
panic('encountered an empty event kind; this is most likely due to using tcc')
}
arr << &KqueueEvent{
fd: fd
kind: kind
}
}
return arr
}
return []
}
// close closes the KqueueNotifier,
// any successive calls to add, modify, remove, and wait should fail
fn (mut kn KqueueNotifier) close() ! {
if C.close(kn.kqueue_fd) == -1 {
return error(os.posix_get_error_msg(C.errno))
}
}
// event_mask_to_flag is a helper function that converts a bitmask
// returned by kevent() wait to FdEventType
fn event_mask_to_flag(filter i16, flags u16) FdEventType {
mut res := FdEventType.read
if filter & notify.kqueue_read != 0 {
res.set(.read)
}
if filter & notify.kqueue_write != 0 {
res.set(.write)
}
if filter & notify.kqueue_exception != 0 {
res.set(.exception)
}
if flags & notify.kqueue_eof != 0 {
res.set(.hangup)
}
if flags & notify.kqueue_error != 0 {
res.set(.error)
}
return res
}
// filter_to_mask is a helper function that converts FdEventType
// to a bitmask used by the C functions
fn filter_to_mask(events FdEventType) i16 {
mut mask := i16(0)
if events.has(.read) {
mask |= notify.kqueue_read
}
if events.has(.write) {
mask |= notify.kqueue_write
}
if events.has(.exception) {
mask |= notify.kqueue_exception
}
if events.has(.peer_hangup) {
panic("Kqueue does not support 'peer_hangup' event type.")
}
if events.has(.error) {
panic("Kqueue does not support 'error' event type.")
}
if events.has(.hangup) {
panic("Kqueue does not support 'hangup' event type.")
}
return mask
}
// flags_to_mask is a helper function that converts FdConfigFlags
// to a bitmask used by the C functions
fn flags_to_mask(confs ...FdConfigFlags) u16 {
mut mask := notify.kqueue_add | notify.kqueue_enable
for conf in confs {
if conf.has(.edge_trigger) {
mask |= notify.kqueue_edge_trigger
}
if conf.has(.one_shot) {
mask |= notify.kqueue_oneshot
}
if conf.has(.wake_up) {
panic("Kqueue does not support 'wake_up' flag.")
}
if conf.has(.exclusive) {
panic("Kqueue does not support 'exclusive' flag.")
}
}
return mask
}

12
vlib/os/notify/kqueue.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef __KQUEUE_H
#define __KQUEUE_H
#include <sys/event.h>
// Due to the renaming of 'struct kevent' and function 'kevent',
// they are wrapped here to avoid conflicts.
int __kevent__(int handle, const struct kevent* changelist, int nchanges, struct kevent* eventlist, int nevents, const struct timespec* timeout) {
return kevent(handle, changelist, nchanges, eventlist, nevents, timeout);
}
#endif

View File

@ -5,7 +5,7 @@ import os.notify
// make a pipe and return the (read, write) file descriptors
fn make_pipe() !(int, int) {
$if linux {
$if linux || macos {
pipefd := [2]int{}
if C.pipe(&pipefd[0]) != 0 {
return error('error ${C.errno}: ' + os.posix_get_error_msg(C.errno))
@ -16,8 +16,8 @@ fn make_pipe() !(int, int) {
}
fn test_level_trigger() {
// currently only linux is supported
$if linux {
// currently only linux and macos are supported
$if linux || macos {
mut notifier := notify.new()!
reader, writer := make_pipe()!
defer {
@ -37,8 +37,8 @@ fn test_level_trigger() {
}
fn test_edge_trigger() {
// currently only linux is supported
$if linux {
// currently only linux and macos are supported
$if linux || macos {
mut notifier := notify.new()!
reader, writer := make_pipe()!
defer {
@ -53,7 +53,27 @@ fn test_edge_trigger() {
os.fd_write(writer, 'foobar')
check_read_event(mut n, reader, 'foo')
assert notifier.wait(0).len == 0
$if linux {
assert notifier.wait(0).len == 0
}
$if macos {
/*
In the kqueue of macos, EV_CLEAR flag represents a clear event,
which is mainly used for pipeline and socket class events. When this flag is set,
kqueue will trigger the corresponding event when the data is readable or writable,
but it is not guaranteed that the event will only be triggered once.
Compared to EPOLLET, EV_CLEAR's behavior varies. In epoll, the edge triggered mode only triggers
an event once when the state changes from unreadable/non writable to readable/writable,
that is, when the data changes from unreadable to readable,
or when the data changes from unreadable to writable. In the kqueue of macos,
EV_CLEAR does not possess this precise edge triggering behavior.
Therefore, in the kqueue of macos, even if the data is not completely read,
it is possible to continue triggering read events. This means that if you don't process all the data,
the next kqueue event notification may still be triggered
*/
// notifier.wait(0).len == 1 or 0
}
os.fd_write(writer, 'baz')
// we do not get an event because there is still data
@ -65,7 +85,7 @@ fn test_edge_trigger() {
}
fn test_one_shot() {
$if linux {
$if linux || macos {
mut notifier := notify.new()!
reader, writer := make_pipe()!
defer {
@ -89,6 +109,7 @@ fn test_one_shot() {
}
}
// Kqueue does not support 'hangup' event type.
fn test_hangup() {
$if linux {
mut notifier := notify.new()!
@ -112,7 +133,7 @@ fn test_hangup() {
}
fn test_write() {
$if linux {
$if linux || macos {
mut notifier := notify.new()!
reader, writer := make_pipe()!
defer {
@ -133,7 +154,7 @@ fn test_write() {
}
fn test_remove() {
$if linux {
$if linux || macos {
mut notifier := notify.new()!
reader, writer := make_pipe()!
defer {

View File

@ -23,7 +23,7 @@ pub mut:
fd int
loop_id int = -1
events u32
cb fn (int, int, voidptr)
cb fn (int, int, voidptr) = unsafe { nil }
// used internally by the kqueue implementation
backend int
}
@ -31,7 +31,7 @@ pub mut:
pub struct Config {
pub:
port int = 8080
cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response)
cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response) = unsafe { nil }
err_cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response, IError) = default_err_cb
user_data voidptr = unsafe { nil }
timeout_secs int = 8
@ -42,7 +42,7 @@ pub:
[heap]
pub struct Picoev {
cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response)
cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response) = unsafe { nil }
err_cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response, IError) = default_err_cb
user_data voidptr = unsafe { nil }

View File

@ -28,7 +28,7 @@ const time_to_test = time.Time{
hour: 21
minute: 23
second: 42
microsecond: 123456
nanosecond: 123456789
unix: 332198622
}
@ -38,6 +38,7 @@ assert '1980-07-11 21:23' == time_to_test.format()
assert '1980-07-11 21:23:42' == time_to_test.format_ss()
assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli()
assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro()
assert '1980-07-11 21:23:42.123456789' == time_to_test.format_ss_nano()
```
You can also parse strings to produce time.Time values,

View File

@ -9,3 +9,30 @@ fn test_custom_format() {
println(date.custom_format(test_str))
}
fn test_hours() {
assert time.parse('2023-08-04 00:00:45')!.custom_format('hh A h a') == '00 AM 0 am'
assert time.parse('2023-08-04 01:00:45')!.custom_format('hh A h a') == '01 AM 1 am'
assert time.parse('2023-08-04 02:00:45')!.custom_format('hh A h a') == '02 AM 2 am'
assert time.parse('2023-08-04 03:00:45')!.custom_format('hh A h a') == '03 AM 3 am'
assert time.parse('2023-08-04 04:00:45')!.custom_format('hh A h a') == '04 AM 4 am'
assert time.parse('2023-08-04 05:00:45')!.custom_format('hh A h a') == '05 AM 5 am'
assert time.parse('2023-08-04 06:00:45')!.custom_format('hh A h a') == '06 AM 6 am'
assert time.parse('2023-08-04 07:00:45')!.custom_format('hh A h a') == '07 AM 7 am'
assert time.parse('2023-08-04 08:00:45')!.custom_format('hh A h a') == '08 AM 8 am'
assert time.parse('2023-08-04 09:00:45')!.custom_format('hh A h a') == '09 AM 9 am'
assert time.parse('2023-08-04 10:00:45')!.custom_format('hh A h a') == '10 AM 10 am'
assert time.parse('2023-08-04 11:00:45')!.custom_format('hh A h a') == '11 AM 11 am'
assert time.parse('2023-08-04 12:00:45')!.custom_format('hh A h a') == '12 PM 12 pm'
assert time.parse('2023-08-04 13:00:45')!.custom_format('hh A h a') == '01 PM 1 pm'
assert time.parse('2023-08-04 14:00:45')!.custom_format('hh A h a') == '02 PM 2 pm'
assert time.parse('2023-08-04 15:00:45')!.custom_format('hh A h a') == '03 PM 3 pm'
assert time.parse('2023-08-04 16:00:45')!.custom_format('hh A h a') == '04 PM 4 pm'
assert time.parse('2023-08-04 17:00:45')!.custom_format('hh A h a') == '05 PM 5 pm'
assert time.parse('2023-08-04 18:00:45')!.custom_format('hh A h a') == '06 PM 6 pm'
assert time.parse('2023-08-04 19:00:45')!.custom_format('hh A h a') == '07 PM 7 pm'
assert time.parse('2023-08-04 20:00:45')!.custom_format('hh A h a') == '08 PM 8 pm'
assert time.parse('2023-08-04 21:00:45')!.custom_format('hh A h a') == '09 PM 9 pm'
assert time.parse('2023-08-04 22:00:45')!.custom_format('hh A h a') == '10 PM 10 pm'
assert time.parse('2023-08-04 23:00:45')!.custom_format('hh A h a') == '11 PM 11 pm'
}

View File

@ -31,3 +31,9 @@ fn test_duration_str() {
assert time.Duration(1 * time.hour + 5 * time.second).str() == '1:00:05'
assert time.Duration(168 * time.hour + 5 * time.minute + 7 * time.second).str() == '168:05:07'
}
fn test_duration_debug() {
assert time.Duration(1 * time.nanosecond).debug() == 'Duration: 1ns'
assert time.Duration(169 * time.hour + 5 * time.minute + 7 * time.second).debug() == 'Duration: 7days, 1h, 5m, 7s'
assert (-time.Duration(169 * time.hour + 5 * time.minute + 7 * time.second)).debug() == 'Duration: - 7days, 1h, 5m, 7s'
}

View File

@ -17,7 +17,7 @@ pub fn (t Time) format_ss() string {
// format_ss_milli returns a date string in "YYYY-MM-DD HH:mm:ss.123" format (24h).
pub fn (t Time) format_ss_milli() string {
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.microsecond / 1000):03d}'
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000_000):03d}'
}
// format_rfc3339 returns a date string in "YYYY-MM-DDTHH:mm:ss.123Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
@ -25,12 +25,17 @@ pub fn (t Time) format_ss_milli() string {
// It is intended to improve consistency and interoperability, when representing and using date and time in Internet protocols.
pub fn (t Time) format_rfc3339() string {
u := t.local_to_utc()
return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.microsecond / 1000):03d}Z'
return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.nanosecond / 1_000_000):03d}Z'
}
// format_ss_micro returns a date string in "YYYY-MM-DD HH:mm:ss.123456" format (24h).
pub fn (t Time) format_ss_micro() string {
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.microsecond:06d}'
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000):06d}'
}
// format_ss_nano returns a date string in "YYYY-MM-DD HH:mm:ss.123456789" format (24h).
pub fn (t Time) format_ss_nano() string {
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.nanosecond:09d}'
}
// hhmm returns a date string in "HH:mm" format (24h).
@ -221,10 +226,12 @@ pub fn (t Time) custom_format(s string) string {
sb.write_string('${t.hour:02}')
}
'h' {
sb.write_string((t.hour % 12).str())
h := if t.hour > 12 { t.hour - 12 } else { t.hour }
sb.write_string(h.str())
}
'hh' {
sb.write_string('${(t.hour % 12):02}')
h := if t.hour > 12 { t.hour - 12 } else { t.hour }
sb.write_string('${h:02}')
}
'm' {
sb.write_string(t.minute.str())
@ -306,17 +313,17 @@ pub fn (t Time) custom_format(s string) string {
}
}
'a' {
if t.hour > 12 {
sb.write_string('pm')
} else {
if t.hour < 12 {
sb.write_string('am')
} else {
sb.write_string('pm')
}
}
'A' {
if t.hour > 12 {
sb.write_string('PM')
} else {
if t.hour < 12 {
sb.write_string('AM')
} else {
sb.write_string('PM')
}
}
else {
@ -379,8 +386,9 @@ pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string {
.hhmm24 { '${t.hour:02d}:${t.minute:02d}' }
.hhmmss12 { '${hour_}:${t.minute:02d}:${t.second:02d} ${tp}' }
.hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' }
.hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.microsecond / 1000):03d}' }
.hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.microsecond:06d}' }
.hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000_000):03d}' }
.hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000):06d}' }
.hhmmss24_nano { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.nanosecond:06d}' }
else { 'unknown enumeration ${fmt_time}' }
}
}

View File

@ -3,19 +3,22 @@ module time
// operator `==` returns true if provided time is equal to time
[inline]
pub fn (t1 Time) == (t2 Time) bool {
return t1.unix == t2.unix && t1.microsecond == t2.microsecond
return t1.unix == t2.unix && t1.nanosecond == t2.nanosecond
}
// operator `<` returns true if provided time is less than time
[inline]
pub fn (t1 Time) < (t2 Time) bool {
return t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond)
return t1.unix < t2.unix || (t1.unix == t2.unix && t1.nanosecond < t2.nanosecond)
}
// Time subtract using operator overloading.
[inline]
pub fn (lhs Time) - (rhs Time) Duration {
lhs_micro := lhs.unix * 1_000_000 + lhs.microsecond
rhs_micro := rhs.unix * 1_000_000 + rhs.microsecond
return (lhs_micro - rhs_micro) * microsecond
// lhs.unix * 1_000_000_000 + i64(lhs.nanosecond) will overflow i64, for years > 3000 .
// Doing the diff first, and *then* multiplying by `second`, is less likely to overflow,
// since lhs and rhs will be likely close to each other.
unixs := i64(lhs.unix - rhs.unix) * second
nanos := lhs.nanosecond - rhs.nanosecond
return unixs + nanos
}

View File

@ -39,7 +39,7 @@ fn test_time1_should_be_same_as_time2() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
t2 := new_time(Time{
year: 2000
@ -48,7 +48,7 @@ fn test_time1_should_be_same_as_time2() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
assert t1 == t2
}
@ -61,9 +61,9 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -71,7 +71,7 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22
minute: 11
second: 3
microsecond: 101
nanosecond: 101
})
t3 := new_time(Time{
year: 2000
@ -80,7 +80,7 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -90,7 +90,7 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22
minute: 11
second: 4
microsecond: 0
nanosecond: 0
})
assert t1 != t2
assert t3 != t4
@ -104,9 +104,9 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22
minute: 11
second: 3
microsecond: 102
nanosecond: 102
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -114,7 +114,7 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22
minute: 11
second: 3
microsecond: 101
nanosecond: 101
})
t3 := new_time(Time{
year: 2000
@ -123,7 +123,7 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22
minute: 11
second: 5
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -133,7 +133,7 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22
minute: 11
second: 4
microsecond: 0
nanosecond: 0
})
assert t1 > t2
assert t3 > t4
@ -147,9 +147,9 @@ fn test_time2_should_be_less_than_time1() {
hour: 22
minute: 11
second: 3
microsecond: 102
nanosecond: 102
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -157,7 +157,7 @@ fn test_time2_should_be_less_than_time1() {
hour: 22
minute: 11
second: 3
microsecond: 101
nanosecond: 101
})
t3 := new_time(Time{
year: 2000
@ -166,7 +166,7 @@ fn test_time2_should_be_less_than_time1() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -176,7 +176,7 @@ fn test_time2_should_be_less_than_time1() {
hour: 22
minute: 11
second: 2
microsecond: 0
nanosecond: 0
})
assert t2 < t1
assert t4 < t3
@ -190,9 +190,9 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22
minute: 11
second: 3
microsecond: 102
nanosecond: 102
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -200,7 +200,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22
minute: 11
second: 3
microsecond: 101
nanosecond: 101
})
t3 := new_time(Time{
year: 2000
@ -209,7 +209,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22
minute: 11
second: 5
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -219,7 +219,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22
minute: 11
second: 4
microsecond: 0
nanosecond: 0
})
assert t1 >= t2
assert t3 >= t4
@ -233,9 +233,9 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -243,7 +243,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
t3 := new_time(Time{
year: 2000
@ -252,7 +252,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -262,7 +262,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
assert t1 >= t2
assert t3 >= t4
@ -276,9 +276,9 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -286,7 +286,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22
minute: 11
second: 3
microsecond: 101
nanosecond: 101
})
t3 := new_time(Time{
year: 2000
@ -295,7 +295,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -305,7 +305,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22
minute: 11
second: 4
microsecond: 0
nanosecond: 0
})
assert t1 <= t2
assert t3 <= t4
@ -319,9 +319,9 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
// Difference is one microsecond
// Difference is one nanosecond
t2 := new_time(Time{
year: 2000
month: 5
@ -329,7 +329,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
t3 := new_time(Time{
year: 2000
@ -338,7 +338,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
// Difference is one second
t4 := new_time(Time{
@ -348,7 +348,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22
minute: 11
second: 3
microsecond: 0
nanosecond: 0
})
assert t1 <= t2
assert t3 <= t4
@ -362,7 +362,7 @@ fn test_time2_copied_from_time1_should_be_equal() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
t2 := new_time(t1)
assert t2 == t1
@ -370,8 +370,8 @@ fn test_time2_copied_from_time1_should_be_equal() {
fn test_subtract() {
d_seconds := 3
d_microseconds := 13
duration := d_seconds * second + d_microseconds * microsecond
d_nanoseconds := 13
duration := d_seconds * second + d_nanoseconds * nanosecond
t1 := new_time(Time{
year: 2000
month: 5
@ -379,9 +379,9 @@ fn test_subtract() {
hour: 22
minute: 11
second: 3
microsecond: 100
nanosecond: 100
})
t2 := unix2(i64(t1.unix) + d_seconds, t1.microsecond + d_microseconds)
t2 := unix_nanosecond(i64(t1.unix) + d_seconds, t1.nanosecond + d_nanoseconds)
d1 := t2 - t1
d2 := t1 - t2
assert d1 > 0

View File

@ -35,13 +35,13 @@ pub fn parse_rfc3339(s string) !Time {
}
// Check if sn is time only
if !parts[0].contains('-') && parts[0].contains(':') {
mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true
hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[0])!
mut hour_, mut minute_, mut second_, mut microsecond_, mut nanosecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, 0, i64(0), true
hour_, minute_, second_, microsecond_, nanosecond_, unix_offset, is_local_time = parse_iso8601_time(parts[0])!
t = new_time(Time{
hour: hour_
minute: minute_
second: second_
microsecond: microsecond_
nanosecond: nanosecond_
})
if is_local_time {
return t // Time is already local time
@ -52,7 +52,7 @@ pub fn parse_rfc3339(s string) !Time {
} else if unix_offset > 0 {
unix_time += unix_offset
}
t = unix2(i64(unix_time), t.microsecond)
t = unix_nanosecond(i64(unix_time), t.nanosecond)
return t
}
@ -171,9 +171,9 @@ pub fn parse_iso8601(s string) !Time {
return error_invalid_time(12, 'malformed date')
}
year, month, day := parse_iso8601_date(parts[0])!
mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true
mut hour_, mut minute_, mut second_, mut microsecond_, mut nanosecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, 0, i64(0), true
if parts.len == 2 {
hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[1])!
hour_, minute_, second_, microsecond_, nanosecond_, unix_offset, is_local_time = parse_iso8601_time(parts[1])!
}
mut t := new_time(
year: year
@ -182,7 +182,7 @@ pub fn parse_iso8601(s string) !Time {
hour: hour_
minute: minute_
second: second_
microsecond: microsecond_
nanosecond: nanosecond_
)
if is_local_time {
return t // Time already local time
@ -193,7 +193,7 @@ pub fn parse_iso8601(s string) !Time {
} else if unix_offset > 0 {
unix_time += unix_offset
}
t = unix2(i64(unix_time), t.microsecond)
t = unix_nanosecond(i64(unix_time), t.nanosecond)
return t
}
@ -237,7 +237,7 @@ fn parse_iso8601_date(s string) !(int, int, int) {
return year, month, day
}
fn parse_iso8601_time(s string) !(int, int, int, int, i64, bool) {
fn parse_iso8601_time(s string) !(int, int, int, int, int, i64, bool) {
hour_ := 0
minute_ := 0
second_ := 0
@ -281,6 +281,7 @@ fn parse_iso8601_time(s string) !(int, int, int, int, i64, bool) {
if count < 4 {
return error_invalid_time(10, 'malformed date')
}
nanosecond_ = microsecond_ * 1000
}
is_local_time := plus_min_z == `a` && count == 4
is_utc := plus_min_z == `Z` && count == 5
@ -300,5 +301,6 @@ fn parse_iso8601_time(s string) !(int, int, int, int, i64, bool) {
if plus_min_z == `+` {
unix_offset *= -1
}
return hour_, minute_, second_, microsecond_, unix_offset, is_local_time
// eprintln('parse_iso8601_time s: $s | hour_: $hour_ | minute_: $minute_ | second_: $second_ | microsecond_: $microsecond_ | nanosecond_: $nanosecond_ | unix_offset: $unix_offset | is_local_time: $is_local_time')
return hour_, minute_, second_, microsecond_, nanosecond_, unix_offset, is_local_time
}

View File

@ -65,11 +65,11 @@ fn test_parse_iso8601() {
]
times := [
[2020, 6, 5, 15, 38, 6, 0],
[2020, 6, 5, 15, 38, 6, 15959],
[2020, 6, 5, 15, 38, 6, 15959],
[2020, 6, 5, 13, 38, 6, 15959],
[2020, 6, 5, 17, 38, 6, 15959],
[2020, 11, 5, 15, 38, 6, 15959],
[2020, 6, 5, 15, 38, 6, 15959000],
[2020, 6, 5, 15, 38, 6, 15959000],
[2020, 6, 5, 13, 38, 6, 15959000],
[2020, 6, 5, 17, 38, 6, 15959000],
[2020, 11, 5, 15, 38, 6, 15959000],
]
for i, format in formats {
t := time.parse_iso8601(format) or {
@ -89,8 +89,8 @@ fn test_parse_iso8601() {
assert t.minute == minute
second := times[i][5]
assert t.second == second
microsecond := times[i][6]
assert t.microsecond == microsecond
nanosecond := times[i][6]
assert t.nanosecond == nanosecond
}
}
@ -107,7 +107,7 @@ fn test_parse_iso8601_local() {
assert t.hour == 15
assert t.minute == 38
assert t.second == 6
assert t.microsecond == 15959
assert t.nanosecond == 15959_000
}
fn test_parse_iso8601_invalid() {
@ -145,7 +145,7 @@ fn test_parse_iso8601_date_only() {
assert t.hour == 0
assert t.minute == 0
assert t.second == 0
assert t.microsecond == 0
assert t.nanosecond == 0
}
fn check_invalid_date(s string) {

View File

@ -53,13 +53,6 @@ pub fn utc() Time {
return solaris_utc()
}
return linux_utc()
/*
// defaults to most common feature, the microsecond precision is not available
// in this API call
t := C.time(0)
_ = C.time(&t)
return unix2(i64(t), 0)
*/
}
// new_time returns a time struct with the calculated Unix time.
@ -90,7 +83,7 @@ pub fn ticks() i64 {
} $else {
ts := C.timeval{}
C.gettimeofday(&ts, 0)
return i64(ts.tv_sec * u64(1000) + (ts.tv_usec / u64(1000)))
return i64(ts.tv_sec * u64(1000) + (ts.tv_usec / u64(1_000)))
}
// t := i64(C.mach_absolute_time())
// # Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t );
@ -105,7 +98,7 @@ pub fn (t Time) str() string {
}
// convert_ctime converts a C time to V time.
fn convert_ctime(t C.tm, microsecond int) Time {
fn convert_ctime(t C.tm, nanosecond int) Time {
return Time{
year: t.tm_year + 1900
month: t.tm_mon + 1
@ -113,7 +106,7 @@ fn convert_ctime(t C.tm, microsecond int) Time {
hour: t.tm_hour
minute: t.tm_min
second: t.tm_sec
microsecond: microsecond
nanosecond: nanosecond
unix: make_unix_time(t)
// for the actual code base when we
// call convert_ctime, it is always

View File

@ -40,15 +40,17 @@ pub const (
// Time contains various time units for a point in time.
pub struct Time {
pub:
year int
month int
day int
hour int
minute int
second int
microsecond int
unix i64
is_local bool // used to make time.now().local().local() == time.now().local()
year int
month int
day int
hour int
minute int
second int
nanosecond int
unix i64
is_local bool // used to make time.now().local().local() == time.now().local()
//
microsecond int [deprecated: 'use t.nanosecond / 1000 instead'; deprecated_after: '2023-08-05']
}
// FormatDelimiter contains different time formats.
@ -59,6 +61,7 @@ pub enum FormatTime {
hhmmss24
hhmmss24_milli
hhmmss24_micro
hhmmss24_nano
no_time
}
@ -99,7 +102,7 @@ pub fn (t Time) smonth() string {
return time.months_string[i * 3..(i + 1) * 3]
}
// unix_time returns the UNIX time.
// unix_time returns the UNIX time with second resolution.
[inline]
pub fn (t Time) unix_time() i64 {
return t.unix
@ -108,18 +111,39 @@ pub fn (t Time) unix_time() i64 {
// unix_time_milli returns the UNIX time with millisecond resolution.
[inline]
pub fn (t Time) unix_time_milli() i64 {
return t.unix * 1000 + (t.microsecond / 1000)
return t.unix * 1_000 + (i64(t.nanosecond) / 1_000_000)
}
// unix_time_micro returns the UNIX time with microsecond resolution.
[inline]
pub fn (t Time) unix_time_micro() i64 {
return t.unix * 1_000_000 + (i64(t.nanosecond) / 1_000)
}
// unix_time_nano returns the UNIX time with nanosecond resolution.
[inline]
pub fn (t Time) unix_time_nano() i64 {
// TODO: use i128 here, when V supports it, since the following expression overflows for years like 3001:
return t.unix * 1_000_000_000 + i64(t.nanosecond)
}
// add returns a new time with the given duration added.
pub fn (t Time) add(d Duration) Time {
microseconds := i64(t.unix) * 1_000_000 + t.microsecond + d.microseconds()
unix := microseconds / 1_000_000
micro := microseconds % 1_000_000
if t.is_local {
return unix2(unix, int(micro)).as_local()
// This expression overflows i64 for big years (and we do not have i128 yet):
// nanos := t.unix * 1_000_000_000 + i64(t.nanosecond) <-
// ... so instead, handle the addition manually in parts ¯\_(ツ)_/¯
mut unixs := t.unix
mut nanos := i64(t.nanosecond) + d.nanoseconds()
unixs += nanos / time.second
nanos = nanos % time.second
if nanos < 0 {
unixs--
nanos += time.second
}
return unix2(unix, int(micro))
if t.is_local {
return unix_nanosecond(unixs, int(nanos)).as_local()
}
return unix_nanosecond(unixs, int(nanos))
}
// add_seconds returns a new time struct with an added number of seconds.
@ -311,9 +335,9 @@ pub fn days_in_month(month int, year int) !int {
return res
}
// debug returns detailed breakdown of time (`Time{ year: YYYY month: MM day: dd hour: HH: minute: mm second: ss microsecond: micros unix: unix }`)
// debug returns detailed breakdown of time (`Time{ year: YYYY month: MM day: dd hour: HH: minute: mm second: ss nanosecond: nanos unix: unix }`)
pub fn (t Time) debug() string {
return 'Time{ year: ${t.year:04} month: ${t.month:02} day: ${t.day:02} hour: ${t.hour:02} minute: ${t.minute:02} second: ${t.second:02} microsecond: ${t.microsecond:06} unix: ${t.unix:07} }'
return 'Time{ year: ${t.year:04} month: ${t.month:02} day: ${t.day:02} hour: ${t.hour:02} minute: ${t.minute:02} second: ${t.second:02} nanosecond: ${t.nanosecond:09} unix: ${t.unix:07} }'
}
// A lot of these are taken from the Go library.
@ -326,6 +350,7 @@ pub const (
second = Duration(1000 * millisecond)
minute = Duration(60 * second)
hour = Duration(60 * minute)
// day = Duration(24 * hour)
infinite = Duration(i64(9223372036854775807))
)
@ -348,23 +373,22 @@ pub fn (d Duration) milliseconds() i64 {
// consider all of them in sub-one intervals
// seconds returns the duration as a floating point number of seconds.
pub fn (d Duration) seconds() f64 {
sec := d / time.second
nsec := d % time.second
return f64(sec) + f64(nsec) / time.second
return f64(d) / f64(time.second)
}
// minutes returns the duration as a floating point number of minutes.
pub fn (d Duration) minutes() f64 {
min := d / time.minute
nsec := d % time.minute
return f64(min) + f64(nsec) / time.minute
return f64(d) / f64(time.minute)
}
// hours returns the duration as a floating point number of hours.
pub fn (d Duration) hours() f64 {
hr := d / time.hour
nsec := d % time.hour
return f64(hr) + f64(nsec) / time.hour
return f64(d) / f64(time.hour)
}
// days returns the duration as a floating point number of days.
pub fn (d Duration) days() f64 {
return f64(d) / f64(time.hour * 24)
}
// str pretty prints the duration
@ -412,6 +436,35 @@ pub fn (d Duration) str() string {
return '${ns}ns'
}
// debug returns a detailed breakdown of the Duration, as: 'Duration: - 50days, 4h, 3m, 7s, 541ms, 78us, 9ns'
pub fn (d Duration) debug() string {
mut res := []string{}
mut x := i64(d)
mut sign := ''
if x < 0 {
sign = '- '
x = -x
}
for label, v in {
'days': 24 * time.hour
'h': time.hour
'm': time.minute
's': time.second
'ms': time.millisecond
'us': time.microsecond
} {
if x > v {
xx := x / v
x = x % v
res << xx.str() + label
}
}
if x > 0 {
res << '${x}ns'
}
return 'Duration: ${sign}${res.join(', ')}'
}
// offset returns time zone UTC offset in seconds.
pub fn offset() int {
t := utc()

View File

@ -3,8 +3,6 @@ import time
fn test_add_to_day_in_the_previous_century() {
a := time.parse_iso8601('1900-01-01')!
aa := a.add_days(180)
dump(a.debug())
dump(aa.debug())
assert aa.ymmdd() == '1900-06-30'
}
@ -23,6 +21,8 @@ fn test_add_to_day_in_the_recent_past() {
fn test_add_to_day_in_the_future_1() {
a := time.parse_iso8601('3000-11-01')!
aa := a.add_days(180)
dump(a.debug())
dump(aa.debug())
assert aa.ymmdd() == '3001-04-30'
}

View File

@ -2,11 +2,10 @@ module time
#include <mach/mach_time.h>
const (
// start_time is needed on Darwin and Windows because of potential overflows
start_time = C.mach_absolute_time()
time_base = init_time_base()
)
// start_time is needed on Darwin and Windows because of potential overflows
const start_time = C.mach_absolute_time()
const time_base = init_time_base()
[typedef]
struct C.mach_timebase_info_data_t {
@ -25,11 +24,6 @@ struct InternalTimeBase {
denom u32 = 1
}
pub struct C.timeval {
tv_sec u64
tv_usec u64
}
fn init_time_base() C.mach_timebase_info_data_t {
tb := C.mach_timebase_info_data_t{}
C.mach_timebase_info(&tb)
@ -62,29 +56,22 @@ fn vpc_now_darwin() u64 {
return (tm - time.start_time) * time.time_base.numer / time.time_base.denom
}
// darwin_now returns a better precision current time for Darwin based operating system
// this should be implemented with native system calls eventually
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
// the microseconds seconds part and converts to local time
// darwin_now returns a better precision current time for macos
fn darwin_now() Time {
// get the high precision time as UTC clock
tv := C.timeval{}
C.gettimeofday(&tv, 0)
// get the high precision time as UTC realtime clock, and use the nanoseconds part
mut ts := C.timespec{}
C.clock_gettime(C.CLOCK_REALTIME, &ts)
loc_tm := C.tm{}
asec := voidptr(&tv.tv_sec)
C.localtime_r(asec, &loc_tm)
return convert_ctime(loc_tm, int(tv.tv_usec))
C.localtime_r(voidptr(&ts.tv_sec), &loc_tm)
return convert_ctime(loc_tm, int(ts.tv_nsec))
}
// darwin_utc returns a better precision current time for Darwin based operating system
// this should be implemented with native system calls eventually
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
// the microseconds seconds part and normal local time to get correct local time
// darwin_utc returns a better precision current time for macos
fn darwin_utc() Time {
// get the high precision time as UTC clock
tv := C.timeval{}
C.gettimeofday(&tv, 0)
return unix2(i64(tv.tv_sec), int(tv.tv_usec))
mut ts := C.timespec{}
C.clock_gettime(C.CLOCK_REALTIME, &ts)
return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec))
}
// dummy to compile with all compilers

View File

@ -36,7 +36,7 @@ pub fn (t Time) local() Time {
}
loc_tm := C.tm{}
C.localtime_r(voidptr(&t.unix), &loc_tm)
return convert_ctime(loc_tm, t.microsecond)
return convert_ctime(loc_tm, t.nanosecond)
}
// in most systems, these are __quad_t, which is an i64
@ -58,7 +58,7 @@ pub fn sys_mono_now() u64 {
} $else {
ts := C.timespec{}
C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
return u64(ts.tv_sec) * 1000000000 + u64(ts.tv_nsec)
return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
}
}
@ -68,7 +68,7 @@ pub fn sys_mono_now() u64 {
fn vpc_now() u64 {
ts := C.timespec{}
C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
return u64(ts.tv_sec) * 1000000000 + u64(ts.tv_nsec)
return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
}
// The linux_* functions are placed here, since they're used on Android as well
@ -83,7 +83,7 @@ fn linux_now() Time {
C.clock_gettime(C.CLOCK_REALTIME, &ts)
loc_tm := C.tm{}
C.localtime_r(voidptr(&ts.tv_sec), &loc_tm)
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000))
return convert_ctime(loc_tm, int(ts.tv_nsec))
}
fn linux_utc() Time {
@ -91,7 +91,7 @@ fn linux_utc() Time {
// and use the nanoseconds part
mut ts := C.timespec{}
C.clock_gettime(C.CLOCK_REALTIME, &ts)
return unix2(i64(ts.tv_sec), int(ts.tv_nsec / 1000))
return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec))
}
// dummy to compile with all compilers
@ -104,12 +104,6 @@ fn win_utc() Time {
return Time{}
}
// dummy to compile with all compilers
pub struct C.timeval {
tv_sec u64
tv_usec u64
}
// return absolute timespec for now()+d
pub fn (d Duration) timespec() C.timespec {
mut ts := C.timespec{}

View File

@ -10,7 +10,7 @@ fn solaris_now() Time {
C.clock_gettime(C.CLOCK_REALTIME, &ts)
loc_tm := C.tm{}
C.localtime_r(voidptr(&ts.tv_sec), &loc_tm)
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000))
return convert_ctime(loc_tm, int(ts.tv_nsec))
}
fn solaris_utc() Time {
@ -18,7 +18,7 @@ fn solaris_utc() Time {
// and use the nanoseconds part
mut ts := C.timespec{}
C.clock_gettime(C.CLOCK_REALTIME, &ts)
return unix2(i64(ts.tv_sec), int(ts.tv_nsec / 1000))
return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec))
}
// dummy to compile with all compilers

View File

@ -1,18 +1,16 @@
import time
import math
const (
time_to_test = time.Time{
year: 1980
month: 7
day: 11
hour: 21
minute: 23
second: 42
microsecond: 123456
unix: 332198622
}
)
const time_to_test = time.Time{
year: 1980
month: 7
day: 11
hour: 21
minute: 23
second: 42
nanosecond: 123456789
unix: 332198622
}
fn test_is_leap_year() {
// 1996 % 4 = 0 and 1996 % 100 > 0
@ -83,6 +81,14 @@ fn test_unix() {
assert t6.second == 29
}
fn test_format_rfc3339() {
// assert '1980-07-11T19:23:42.123Z'
res := time_to_test.format_rfc3339()
assert res.ends_with('23:42.123Z')
assert res.starts_with('1980-07-1')
assert res.contains('T')
}
fn test_format_ss() {
assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy)
}
@ -93,20 +99,18 @@ fn test_format_ss_milli() {
assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli()
}
fn test_format_rfc3339() {
// assert '1980-07-11T19:23:42.123Z'
res := time_to_test.format_rfc3339()
assert res.ends_with('23:42.123Z')
assert res.starts_with('1980-07-1')
assert res.contains('T')
}
fn test_format_ss_micro() {
assert '11.07.1980 21:23:42.123456' == time_to_test.get_fmt_str(.dot, .hhmmss24_micro,
.ddmmyyyy)
assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro()
}
fn test_format_ss_nano() {
assert '11.07.1980 21:23:42.123456789' == time_to_test.get_fmt_str(.dot, .hhmmss24_nano,
.ddmmyyyy)
assert '1980-07-11 21:23:42.123456789' == time_to_test.format_ss_nano()
}
fn test_smonth() {
month_names := ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov',
'Dec']
@ -180,21 +184,29 @@ fn test_weekday_str() {
fn test_add() {
d_seconds := 3
d_microseconds := 13
duration := time.Duration(d_seconds * time.second + d_microseconds * time.microsecond)
d_nanoseconds := 13
duration := time.Duration(d_seconds * time.second + d_nanoseconds * time.nanosecond)
// dump(duration.debug())
t1 := time_to_test
// dump(t1.debug())
t2 := time_to_test.add(duration)
// dump(t2.debug())
assert t2.second == t1.second + d_seconds
assert t2.microsecond == t1.microsecond + d_microseconds
assert t2.nanosecond == t1.nanosecond + d_nanoseconds
assert t2.unix == t1.unix + d_seconds
assert t2.is_local == t1.is_local
//
t3 := time_to_test.add(-duration)
// dump(t3.debug())
assert t3.second == t1.second - d_seconds
assert t3.microsecond == t1.microsecond - d_microseconds
assert t3.nanosecond == t1.nanosecond - d_nanoseconds
assert t3.unix == t1.unix - d_seconds
assert t3.is_local == t1.is_local
//
t4 := time_to_test.as_local()
// dump(t4.debug())
t5 := t4.add(duration)
// dump(t5.debug())
assert t5.is_local == t4.is_local
}
@ -220,13 +232,14 @@ fn test_now() {
assert now.minute < 60
assert now.second >= 0
assert now.second <= 60 // <= 60 cause of leap seconds
assert now.microsecond >= 0
assert now.microsecond < 1000000
assert now.nanosecond >= 0
assert now.nanosecond < time.second
}
fn test_utc() {
now := time.utc()
// The year the test was built
// dump(now.debug())
assert now.year >= 2020
assert now.month > 0
assert now.month <= 12
@ -234,20 +247,20 @@ fn test_utc() {
assert now.minute < 60
assert now.second >= 0
assert now.second <= 60 // <= 60 cause of leap seconds
assert now.microsecond >= 0
assert now.microsecond < 1000000
assert now.nanosecond >= 0
assert now.nanosecond < time.second
}
fn test_unix_time() {
t1 := time.utc()
time.sleep(50 * time.millisecond)
t2 := time.utc()
eprintln('t1: ${t1}')
eprintln('t2: ${t2}')
eprintln(' t1: ${t1}')
eprintln(' t2: ${t2}')
ut1 := t1.unix_time()
ut2 := t2.unix_time()
eprintln('ut1: ${ut1}')
eprintln('ut2: ${ut2}')
eprintln(' ut1: ${ut1}')
eprintln(' ut2: ${ut2}')
assert ut2 - ut1 < 2
//
utm1 := t1.unix_time_milli()

View File

@ -39,6 +39,8 @@ fn C.SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation &C.TIME_ZONE_INFORMAT
fn C.localtime_s(t &C.time_t, tm &C.tm)
fn C.timespec_get(t &C.timespec, base int) int
const (
// start_time is needed on Darwin and Windows because of potential overflows
start_time = init_win_time_start()
@ -107,7 +109,7 @@ pub fn (t Time) local() Time {
hour: u16(t.hour)
minute: u16(t.minute)
second: u16(t.second)
millisecond: u16(t.microsecond / 1000)
millisecond: u16(t.nanosecond / 1_000_000)
}
st_local := SystemTime{}
C.SystemTimeToTzSpecificLocalTime(unsafe { nil }, &st_utc, &st_local)
@ -118,7 +120,7 @@ pub fn (t Time) local() Time {
hour: st_local.hour
minute: st_local.minute
second: st_local.second // These are the same
microsecond: int(st_local.millisecond) * 1000
nanosecond: int(st_local.millisecond) * 1_000_000
unix: st_local.unix_time()
}
return t_local
@ -141,7 +143,7 @@ fn win_now() Time {
hour: st_local.hour
minute: st_local.minute
second: st_local.second
microsecond: int(st_local.millisecond) * 1000
nanosecond: int(st_local.millisecond) * 1_000_000
unix: st_local.unix_time()
is_local: true
}
@ -163,7 +165,7 @@ fn win_utc() Time {
hour: st_utc.hour
minute: st_utc.minute
second: st_utc.second
microsecond: int(st_utc.millisecond) * 1000
nanosecond: int(st_utc.millisecond) * 1_000_000
unix: st_utc.unix_time()
is_local: false
}
@ -213,12 +215,6 @@ fn solaris_utc() Time {
return Time{}
}
// dummy to compile with all compilers
pub struct C.timeval {
tv_sec u64
tv_usec u64
}
// sleep makes the calling thread sleep for a given duration (in nanoseconds).
pub fn sleep(duration Duration) {
C.Sleep(int(duration / millisecond))

View File

@ -3,7 +3,7 @@
// that can be found in the LICENSE file.
module time
// unix returns a time struct from Unix time.
// unix returns a time struct from an Unix timestamp (number of seconds since 1970-01-01)
pub fn unix(abs i64) Time {
// Split into day and time
mut day_offset := abs / seconds_per_day
@ -24,8 +24,20 @@ pub fn unix(abs i64) Time {
}
}
// unix2 returns a time struct from Unix time and microsecond value
// unix2 returns a Time struct, given an Unix timestamp in seconds, and a microsecond value
[deprecated: 'use unix_microsecond(unix_ts, us) instead']
[deprecated_after: '2023-09-05']
pub fn unix2(abs i64, microsecond int) Time {
return unix_nanosecond(abs, microsecond * 1000)
}
// unix_microsecond returns a Time struct, given an Unix timestamp in seconds, and a microsecond value
pub fn unix_microsecond(abs i64, microsecond int) Time {
return unix_nanosecond(abs, microsecond * 1000)
}
// unix_nanosecond returns a Time struct, given an Unix timestamp in seconds, and a nanosecond value
pub fn unix_nanosecond(abs i64, nanosecond int) Time {
// Split into day and time
mut day_offset := abs / seconds_per_day
if abs % seconds_per_day < 0 {
@ -41,7 +53,7 @@ pub fn unix2(abs i64, microsecond int) Time {
hour: hr
minute: min
second: sec
microsecond: microsecond
nanosecond: nanosecond
unix: abs
}
}

View File

@ -89,6 +89,13 @@ pub fn (t &Table) stringify_fn_decl(node &FnDecl, cur_mod string, m2a map[string
f.write_string('pub ')
}
f.write_string('fn ')
pre_comments := node.comments.filter(it.pos.pos < node.name_pos.pos)
if pre_comments.len > 0 {
write_comments(pre_comments, mut f)
if !f.last_n(1)[0].is_space() {
f.write_string(' ')
}
}
if node.is_method {
f.write_string('(')
mut styp := util.no_cur_mod(t.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
@ -126,6 +133,7 @@ pub fn (t &Table) stringify_fn_decl(node &FnDecl, cur_mod string, m2a map[string
fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_mod string, m2a map[string]string) {
mut add_para_types := true
mut is_wrap_needed := false
if node.generic_names.len > 0 {
if node.is_method {
sym := t.sym(node.params[0].typ)
@ -161,6 +169,20 @@ fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_m
is_type_only := param.name == ''
should_add_type := true // is_last_param || is_type_only || node.params[i + 1].typ != param.typ ||
// (node.is_variadic && i == node.params.len - 2)
pre_comments := param.comments.filter(it.pos.pos < param.pos.pos)
if pre_comments.len > 0 {
if i == 0 && !pre_comments.last().is_inline {
is_wrap_needed = true
f.write_string('\n\t')
}
write_comments(pre_comments, mut f)
if !f.last_n(1)[0].is_space() {
f.write_string(' ')
}
}
if is_wrap_needed {
f.write_string('\t')
}
if param.is_mut {
f.write_string(param.typ.share().str() + ' ')
}
@ -211,13 +233,50 @@ fn (t &Table) stringify_fn_after_name(node &FnDecl, mut f strings.Builder, cur_m
}
}
fn write_comments(comments []Comment, mut f strings.Builder) {
for i, c in comments {
if !f.last_n(1)[0].is_space() {
f.write_string(' ')
}
write_comment(c, mut f)
if c.is_inline && i < comments.len - 1 && !c.is_multi {
f.write_string(' ')
} else if (!c.is_inline || c.is_multi) && i < comments.len - 1 {
f.writeln('')
}
}
}
fn write_comment(node Comment, mut f strings.Builder) {
if node.is_inline {
x := node.text.trim_left('\x01').trim_space()
if x.contains('\n') {
f.writeln('/*')
f.writeln(x)
f.write_string('*/')
} else {
f.write_string('/* ${x} */')
}
} else {
mut s := node.text.trim_left('\x01').trim_right(' ')
mut out_s := '//'
if s != '' {
if s[0].is_letter() || s[0].is_digit() {
out_s += ' '
}
out_s += s
}
f.writeln(out_s)
}
}
struct StringifyModReplacement {
mod string
alias string
weight int
}
pub fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) string {
fn shorten_full_name_based_on_aliases(input string, m2a map[string]string) string {
if m2a.len == 0 || -1 == input.index_u8(`.`) {
// a simple typename, like `string` or `[]bool`; no module aliasings apply,
// (or there just are not any mappings)

View File

@ -1779,7 +1779,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
// cannot hide interface expected type to make possible to pass its interface type automatically
earg_types << if targ.idx() != param.typ.idx() { param.typ } else { targ }
} else {
earg_types << targ
earg_types << param.typ
}
param_share := param.typ.share()
if param_share == .shared_t

View File

@ -1019,7 +1019,7 @@ pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) {
pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
f.attrs(node.attrs)
f.fn_header(node)
f.write(f.table.stringify_fn_decl(&node, f.cur_mod, f.mod2alias))
// Handle trailing comments after fn header declarations
if node.no_body && node.end_comments.len > 0 {
first_comment := node.end_comments[0]
@ -1043,136 +1043,6 @@ pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
f.fn_body(node)
}
pub fn (mut f Fmt) fn_header(node ast.FnDecl) {
if node.is_pub {
f.write('pub ')
}
f.write('fn ')
pre_comments := node.comments.filter(it.pos.pos < node.name_pos.pos)
if pre_comments.len > 0 {
f.comments(pre_comments)
f.write(' ')
}
if node.is_method {
f.write('(')
mut styp := util.no_cur_mod(f.table.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
f.cur_mod)
if node.rec_mut {
f.write(node.receiver.typ.share().str() + ' ')
styp = styp[1..] // remove &
}
f.write(node.receiver.name + ' ')
styp = util.no_cur_mod(styp, f.cur_mod)
if node.params[0].is_auto_rec {
styp = styp.trim('&')
}
f.write(styp + ') ')
} else if node.is_static_type_method {
mut styp := util.no_cur_mod(f.table.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
f.cur_mod)
f.write(styp + '.')
}
mut name := if !node.is_method && node.language == .v {
node.name.all_after_last('.')
} else {
node.name
}
if node.is_static_type_method {
name = name.after('__static__')
}
f.write(name)
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
f.write(' ')
}
mut add_para_types := true
if node.generic_names.len > 0 {
if node.is_method {
sym := f.table.sym(node.params[0].typ)
if sym.info is ast.Struct {
generic_names := sym.info.generic_types.map(f.table.sym(it).name)
if generic_names == node.generic_names {
add_para_types = false
}
}
}
if add_para_types {
f.write('[')
for i, gname in node.generic_names {
is_last := i == node.generic_names.len - 1
f.write(gname)
if !is_last {
f.write(', ')
}
}
f.write(']')
}
}
f.write('(')
for i, arg in node.params {
before_comments := arg.comments.filter(it.pos.pos < arg.pos.pos)
if before_comments.len > 0 {
f.comments(before_comments, level: .indent)
}
// skip receiver
if node.is_method && i == 0 {
continue
}
if arg.is_hidden {
continue
}
is_last_arg := i == node.params.len - 1
is_type_only := arg.name == ''
should_add_type := true
if arg.is_mut {
f.write(arg.typ.share().str() + ' ')
}
f.write(arg.name)
arg_sym := f.table.sym(arg.typ)
if arg_sym.kind == .struct_ && (arg_sym.info as ast.Struct).is_anon {
f.write(' struct {')
struct_ := arg_sym.info as ast.Struct
for field in struct_.fields {
f.write(' ${field.name} ${f.table.type_to_str(field.typ)}')
if field.has_default_expr {
f.write(' = ${field.default_expr}')
}
}
if struct_.fields.len > 0 {
f.write(' ')
}
f.write('}')
} else {
mut s := f.table.type_to_str(arg.typ.clear_flag(.shared_f))
if arg.is_mut {
if s.starts_with('&') && ((!arg_sym.is_number() && arg_sym.kind != .bool)
|| node.language != .v) {
s = s[1..]
}
}
s = util.no_cur_mod(s, f.cur_mod)
s = ast.shorten_full_name_based_on_aliases(s, f.mod2alias)
if should_add_type {
if !is_type_only {
f.write(' ')
}
if node.is_variadic && is_last_arg {
f.write('...')
}
f.write(s)
}
}
if !is_last_arg {
f.write(', ')
}
}
f.write(')')
if node.return_type != ast.void_type {
sreturn_type := util.no_cur_mod(f.table.type_to_str(node.return_type), f.cur_mod)
short_sreturn_type := ast.shorten_full_name_based_on_aliases(sreturn_type, f.mod2alias)
f.write(' ${short_sreturn_type}')
}
}
pub fn (mut f Fmt) anon_fn(node ast.AnonFn) {
f.write(f.table.stringify_anon_decl(&node, f.cur_mod, f.mod2alias)) // `Expr` instead of `ast.Expr` in mod ast
f.fn_body(node.decl)

View File

@ -3,6 +3,6 @@ fn /* main */ main() {
}
fn // hi
print_hi() {
print_hi() {
println('hi')
}

View File

@ -0,0 +1,8 @@
fn foo(
// Foo
s string) {
}
fn bar( /* p1 */ a string, /* p2 */ b int) {
println('hello')
}

View File

@ -0,0 +1,8 @@
fn foo(
// Foo
s string) {
}
fn bar(/*p1*/a string, /*p2*/b int) {
println('hello')
}

View File

@ -11,7 +11,12 @@ recognized by the heuristic:
tool_version = '1.2.1'
version: '0.2.42'
VERSION = "1.23.8"
If certain lines need to be skipped, use the --skip option. For instance,
the following command will skip lines containing "tool-version":
v bump --patch --skip "tool-version" [files...]
Examples:
Bump the patch version in v.mod if it exists
v bump --patch
@ -22,7 +27,8 @@ Examples:
Options:
-h, --help Show this help text.
-m, --major Bump the major version.
-n, --minor Bump the minor version.
-p, --patch Bump the patch version.
-h, --help Show this help text.
-m, --major Bump the major version.
-n, --minor Bump the minor version.
-p, --patch Bump the patch version.
-s, --skip <string> Skip lines matching this substring.

View File

@ -0,0 +1,18 @@
struct Foo {
f fn (Foo) int = dummy
}
fn dummy(s Foo) int {
return 22
}
fn (mut s Foo) fun() int {
return s.f(s)
}
fn test_struct_field_fn_call() {
mut s := Foo{}
ret := s.fun()
println(ret)
assert ret == 22
}