mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
Compare commits
15 Commits
weekly.202
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
65a493d023 | ||
![]() |
76b4c92848 | ||
![]() |
d0cc564089 | ||
![]() |
f915366ac4 | ||
![]() |
3211a653c3 | ||
![]() |
eef9b5f168 | ||
![]() |
64029a2980 | ||
![]() |
b7afe6b236 | ||
![]() |
6813a12339 | ||
![]() |
10df697d32 | ||
![]() |
68f18fcb8e | ||
![]() |
f4859ffb11 | ||
![]() |
3b3395d93b | ||
![]() |
8db1aaafd5 | ||
![]() |
286d39706b |
@ -197,9 +197,14 @@ body {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
.doc-nav > .heading-container > .heading > #search {
|
||||
.doc-nav #search {
|
||||
position: relative;
|
||||
margin: 0.6rem 1.2rem 1rem 1.2rem;
|
||||
display: flex;
|
||||
}
|
||||
.doc-nav #search input {
|
||||
border: none;
|
||||
width: 100%;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.5rem 1rem;
|
||||
outline: none;
|
||||
@ -208,17 +213,32 @@ body {
|
||||
color: #fff;
|
||||
color: var(--menu-search-text-color);
|
||||
}
|
||||
.doc-nav > .heading-container > .heading > #search::placeholder {
|
||||
.doc-nav #search input::placeholder {
|
||||
color: #edf2f7;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-nav > .heading-container > .heading > #search:-ms-input-placeholder {
|
||||
color: #edf2f7;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
.doc-nav #search-keys {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
top: 0;
|
||||
right: 0.75rem;
|
||||
opacity: 0.33;
|
||||
transition: opacity 0.1s;
|
||||
}
|
||||
.doc-nav #search-keys.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
.doc-nav #search-keys kbd {
|
||||
padding: 2.5px 3px;
|
||||
margin-left: 1px;
|
||||
font-size: 11px;
|
||||
background-color: var(--menu-background-color);
|
||||
border: 1px solid #ffffff44;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.doc-nav > .content {
|
||||
padding: 0 2rem 2rem 2rem;
|
||||
@ -278,7 +298,8 @@ body {
|
||||
.doc-nav .search li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.doc-nav > .search .result:hover {
|
||||
.doc-nav > .search .result:hover,
|
||||
.doc-nav > .search .result.selected {
|
||||
background-color: #00000021;
|
||||
background-color: var(--menu-search-result-background-hover-color);
|
||||
}
|
||||
|
@ -49,7 +49,6 @@ function setupMobileToggle() {
|
||||
const isHidden = docNav.classList.contains('hidden');
|
||||
docNav.classList.toggle('hidden');
|
||||
const search = docNav.querySelector('.search');
|
||||
// console.log(search);
|
||||
const searchHasResults = search.classList.contains('has-results');
|
||||
if (isHidden && searchHasResults) {
|
||||
search.classList.remove('mobile-hidden');
|
||||
@ -145,6 +144,72 @@ function setupSearch() {
|
||||
}
|
||||
});
|
||||
searchInput.addEventListener('input', onInputChange);
|
||||
setupSearchKeymaps();
|
||||
}
|
||||
|
||||
function setupSearchKeymaps() {
|
||||
const searchInput = document.querySelector('#search input');
|
||||
// Keyboard shortcut indicator
|
||||
const searchKeys = document.createElement('div');
|
||||
const modifierKeyPrefix = navigator.platform.includes('Mac') ? '⌘' : 'Ctrl';
|
||||
searchKeys.setAttribute('id', 'search-keys');
|
||||
searchKeys.innerHTML = '<kbd>' + modifierKeyPrefix + '</kbd><kbd>k</kbd>';
|
||||
searchInput.parentElement?.appendChild(searchKeys);
|
||||
searchInput.addEventListener('focus', () => searchKeys.classList.add('hide'));
|
||||
searchInput.addEventListener('blur', () => searchKeys.classList.remove('hide'));
|
||||
// Global shortcuts to focus searchInput
|
||||
document.addEventListener('keydown', (ev) => {
|
||||
if (ev.key === '/' || ((ev.ctrlKey || ev.metaKey) && ev.key === 'k')) {
|
||||
ev.preventDefault();
|
||||
searchInput.focus();
|
||||
}
|
||||
});
|
||||
// Shortcuts while searchInput is focused
|
||||
let selectedIdx = -1;
|
||||
function selectResult(results, newIdx) {
|
||||
if (selectedIdx !== -1) {
|
||||
results[selectedIdx].classList.remove('selected');
|
||||
}
|
||||
results[newIdx].classList.add('selected');
|
||||
results[newIdx].scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' });
|
||||
selectedIdx = newIdx;
|
||||
}
|
||||
searchInput.addEventListener('keydown', (ev) => {
|
||||
const searchResults = document.querySelectorAll('.search .result');
|
||||
switch (ev.key) {
|
||||
case 'Escape':
|
||||
searchInput.blur();
|
||||
break;
|
||||
case 'Enter':
|
||||
if (!searchResults.length || selectedIdx === -1) break;
|
||||
searchResults[selectedIdx].querySelector('a').click();
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
ev.preventDefault();
|
||||
if (!searchResults.length) break;
|
||||
if (selectedIdx >= searchResults.length - 1) {
|
||||
// Cycle to first if last is selected
|
||||
selectResult(searchResults, 0);
|
||||
} else {
|
||||
// Select next
|
||||
selectResult(searchResults, selectedIdx + 1);
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
ev.preventDefault();
|
||||
if (!searchResults.length) break;
|
||||
if (selectedIdx <= 0) {
|
||||
// Cycle to last if first is selected (or select it if none is selcted yet)
|
||||
selectResult(searchResults, searchResults.length - 1);
|
||||
} else {
|
||||
// Select previous
|
||||
selectResult(searchResults, selectedIdx - 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
selectedIdx = -1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createSearchResult(data) {
|
||||
@ -194,11 +259,3 @@ function debounce(func, timeout) {
|
||||
timer = setTimeout(next, timeout > 0 ? timeout : 300);
|
||||
};
|
||||
}
|
||||
|
||||
document.addEventListener('keypress', (ev) => {
|
||||
if (ev.key == '/') {
|
||||
const search = document.getElementById('search');
|
||||
ev.preventDefault();
|
||||
search.focus();
|
||||
}
|
||||
});
|
||||
|
@ -46,7 +46,9 @@
|
||||
</div>
|
||||
{{ menu_icon }}
|
||||
</div>
|
||||
<input type="text" id="search" placeholder="Search... (beta)" autocomplete="off" />
|
||||
<div id="search">
|
||||
<input type="text" placeholder="Search... (beta)" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="search hidden"></nav>
|
||||
|
@ -324,8 +324,8 @@ fn vpm_install_from_vcs(module_names []string, vcs_key string) {
|
||||
eprintln('Removing module "${minfo.final_module_path}" ...')
|
||||
os.rmdir_all(minfo.final_module_path) or {
|
||||
errors++
|
||||
println('Errors while removing "${minfo.final_module_path}" :')
|
||||
println(err)
|
||||
eprintln('Errors while removing "${minfo.final_module_path}" :')
|
||||
eprintln(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -342,6 +342,14 @@ fn vpm_install_from_vcs(module_names []string, vcs_key string) {
|
||||
continue
|
||||
}
|
||||
println('Module "${name}" relocated to "${vmod_.name}" successfully.')
|
||||
publisher_dir := final_module_path.all_before_last(os.path_separator)
|
||||
if os.is_dir_empty(publisher_dir) {
|
||||
os.rmdir(publisher_dir) or {
|
||||
errors++
|
||||
eprintln('Errors while removing "${publisher_dir}" :')
|
||||
eprintln(err)
|
||||
}
|
||||
}
|
||||
final_module_path = minfo.final_module_path
|
||||
}
|
||||
name = vmod_.name
|
||||
|
@ -2,20 +2,19 @@ import db.sqlite
|
||||
|
||||
fn main() {
|
||||
db := sqlite.connect(':memory:')!
|
||||
db.exec("create table users (id integer primary key, name text default '');")
|
||||
db.exec("create table users (id integer primary key, name text default '');") or { panic(err) }
|
||||
|
||||
db.exec("insert into users (name) values ('Sam')")
|
||||
db.exec("insert into users (name) values ('Peter')")
|
||||
db.exec("insert into users (name) values ('Kate')")
|
||||
db.exec("insert into users (name) values ('Sam')")!
|
||||
db.exec("insert into users (name) values ('Peter')")!
|
||||
db.exec("insert into users (name) values ('Kate')")!
|
||||
|
||||
nr_users := db.q_int('select count(*) from users')
|
||||
nr_users := db.q_int('select count(*) from users')!
|
||||
println('nr users = ${nr_users}')
|
||||
|
||||
name := db.q_string('select name from users where id = 1')
|
||||
name := db.q_string('select name from users where id = 1')!
|
||||
assert name == 'Sam'
|
||||
|
||||
users, code := db.exec('select * from users')
|
||||
println('SQL Result code: ${code}')
|
||||
users := db.exec('select * from users')!
|
||||
for row in users {
|
||||
println(row.vals)
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ pub fn (mut app App) index() vweb.Result {
|
||||
|
||||
fn (mut app App) update_db() !int {
|
||||
mut db := mydb()!
|
||||
db.exec('INSERT INTO visits (created_at) VALUES ("")')
|
||||
visits := db.q_int('SELECT count(*) FROM visits')
|
||||
db.exec('INSERT INTO visits (created_at) VALUES ("")')!
|
||||
visits := db.q_int('SELECT count(*) FROM visits')!
|
||||
db.close()!
|
||||
return visits
|
||||
}
|
||||
@ -56,10 +56,10 @@ fn main() {
|
||||
println('`v -d vweb_livereload watch --keep run examples/vwatch/web_server/`')
|
||||
println('')
|
||||
mut db := mydb()!
|
||||
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')
|
||||
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')!
|
||||
db.exec('CREATE TRIGGER INSERT_visits AFTER INSERT ON visits BEGIN
|
||||
UPDATE visits SET created_at = datetime("now", "localtime") WHERE rowid = new.rowid ;
|
||||
END')
|
||||
END')!
|
||||
db.close()!
|
||||
vweb.run(&App{}, 19123)
|
||||
}
|
||||
|
@ -286,8 +286,7 @@ pub fn winapi_lasterr_str() string {
|
||||
}
|
||||
mut msgbuf := &u16(0)
|
||||
res := C.FormatMessage(C.FORMAT_MESSAGE_ALLOCATE_BUFFER | C.FORMAT_MESSAGE_FROM_SYSTEM | C.FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
C.NULL, err_msg_id, C.MAKELANGID(C.LANG_NEUTRAL, C.SUBLANG_DEFAULT), &msgbuf,
|
||||
0, C.NULL)
|
||||
0, err_msg_id, 0, voidptr(&msgbuf), 0, 0)
|
||||
err_msg := if res == 0 {
|
||||
'Win-API error ${err_msg_id}'
|
||||
} else {
|
||||
|
@ -349,7 +349,7 @@ fn C.FindClose(hFindFile voidptr)
|
||||
// macro
|
||||
fn C.MAKELANGID(lgid voidptr, srtid voidptr) int
|
||||
|
||||
fn C.FormatMessage(dwFlags u32, lpSource voidptr, dwMessageId u32, dwLanguageId u32, lpBuffer voidptr, nSize int, arguments ...voidptr) voidptr
|
||||
fn C.FormatMessage(dwFlags u32, lpSource voidptr, dwMessageId u32, dwLanguageId u32, lpBuffer voidptr, nSize u32, arguments ...voidptr) u32
|
||||
|
||||
fn C.CloseHandle(voidptr) int
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
module mysql
|
||||
|
||||
pub struct Result {
|
||||
pub:
|
||||
result &C.MYSQL_RES = unsafe { nil }
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,6 @@ For instance:
|
||||
import db.sqlite
|
||||
|
||||
db := sqlite.connect('foo.db') or { panic(err) }
|
||||
db.synchronization_mode(sqlite.SyncMode.off)
|
||||
db.journal_mode(sqlite.JournalMode.memory)
|
||||
db.synchronization_mode(sqlite.SyncMode.off)!
|
||||
db.journal_mode(sqlite.JournalMode.memory)!
|
||||
```
|
||||
|
@ -75,7 +75,7 @@ pub fn (db DB) delete(table string, where orm.QueryData) ! {
|
||||
pub fn (db DB) last_id() int {
|
||||
query := 'SELECT last_insert_rowid();'
|
||||
|
||||
return db.q_int(query)
|
||||
return db.q_int(query) or { 0 }
|
||||
}
|
||||
|
||||
// DDL (table creation/destroying etc)
|
||||
|
@ -131,7 +131,7 @@ pub fn connect(path string) !DB {
|
||||
code := C.sqlite3_open(&char(path.str), &db)
|
||||
if code != 0 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
@ -150,7 +150,7 @@ pub fn (mut db DB) close() !bool {
|
||||
db.is_open = false
|
||||
} else {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
@ -180,41 +180,50 @@ pub fn (db &DB) get_affected_rows_count() int {
|
||||
return C.sqlite3_changes(db.conn)
|
||||
}
|
||||
|
||||
// q_int returns a single integer value, from the first column of the result of executing `query`
|
||||
pub fn (db &DB) q_int(query string) int {
|
||||
// q_int returns a single integer value, from the first column of the result of executing `query`, or an error on failure
|
||||
pub fn (db &DB) q_int(query string) !int {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
C.sqlite3_step(stmt)
|
||||
code := C.sqlite3_step(stmt)
|
||||
if code != sqlite.sqlite_row {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
|
||||
res := C.sqlite3_column_int(stmt, 0)
|
||||
return res
|
||||
}
|
||||
|
||||
// q_string returns a single string value, from the first column of the result of executing `query`
|
||||
pub fn (db &DB) q_string(query string) string {
|
||||
// q_string returns a single string value, from the first column of the result of executing `query`, or an error on failure
|
||||
pub fn (db &DB) q_string(query string) !string {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
C.sqlite3_step(stmt)
|
||||
code := C.sqlite3_step(stmt)
|
||||
if code != sqlite.sqlite_row {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
|
||||
return if val != &u8(0) { unsafe { tos_clone(val) } } else { '' }
|
||||
}
|
||||
|
||||
// exec executes the query on the given `db`, and returns an array of all the results, alongside any result code.
|
||||
// Result codes: https://www.sqlite.org/rescode.html
|
||||
// exec executes the query on the given `db`, and returns an array of all the results, or an error on failure
|
||||
[manualfree]
|
||||
pub fn (db &DB) exec(query string) ([]Row, int) {
|
||||
pub fn (db &DB) exec(query string) ![]Row {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
if code != sqlite.sqlite_ok {
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
|
||||
nr_cols := C.sqlite3_column_count(stmt)
|
||||
mut res := 0
|
||||
mut rows := []Row{}
|
||||
@ -236,26 +245,21 @@ pub fn (db &DB) exec(query string) ([]Row, int) {
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
return rows, res
|
||||
return rows
|
||||
}
|
||||
|
||||
// exec_one executes a query on the given `db`.
|
||||
// It returns either the first row from the result, if the query was successful, or an error.
|
||||
[manualfree]
|
||||
pub fn (db &DB) exec_one(query string) !Row {
|
||||
rows, code := db.exec(query)
|
||||
rows := db.exec(query)!
|
||||
defer {
|
||||
unsafe { rows.free() }
|
||||
}
|
||||
if rows.len == 0 {
|
||||
return &SQLError{
|
||||
msg: 'No rows'
|
||||
code: code
|
||||
}
|
||||
} else if code != 101 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
code: code
|
||||
code: sqlite.sqlite_done
|
||||
}
|
||||
}
|
||||
res := rows[0]
|
||||
@ -295,21 +299,13 @@ pub fn (db &DB) exec_param_many(query string, params []string) ![]Row {
|
||||
|
||||
mut code := C.sqlite3_prepare_v2(db.conn, &char(query.str), -1, &stmt, 0)
|
||||
if code != 0 {
|
||||
return &SQLError{
|
||||
msg: unsafe {
|
||||
cstring_to_vstring(&char(C.sqlite3_errstr(code)))
|
||||
}
|
||||
code: code
|
||||
}
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
|
||||
for i, param in params {
|
||||
code = C.sqlite3_bind_text(stmt, i + 1, voidptr(param.str), param.len, 0)
|
||||
if code != 0 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
code: code
|
||||
}
|
||||
return db.error_message(code, query)
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,8 +341,8 @@ pub fn (db &DB) exec_param(query string, param string) ![]Row {
|
||||
// create_table issues a "create table if not exists" command to the db.
|
||||
// It creates the table named 'table_name', with columns generated from 'columns' array.
|
||||
// The default columns type will be TEXT.
|
||||
pub fn (db &DB) create_table(table_name string, columns []string) {
|
||||
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')
|
||||
pub fn (db &DB) create_table(table_name string, columns []string) ! {
|
||||
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')!
|
||||
}
|
||||
|
||||
// busy_timeout sets a busy timeout in milliseconds.
|
||||
@ -359,37 +355,39 @@ pub fn (db &DB) busy_timeout(ms int) int {
|
||||
|
||||
// synchronization_mode sets disk synchronization mode, which controls how
|
||||
// aggressively SQLite will write data to physical storage.
|
||||
// If the command fails to execute an error is returned
|
||||
// .off: No syncs at all. (fastest)
|
||||
// .normal: Sync after each sequence of critical disk operations.
|
||||
// .full: Sync after each critical disk operation (slowest).
|
||||
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
|
||||
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) ! {
|
||||
if sync_mode == .off {
|
||||
db.exec('pragma synchronous = OFF;')
|
||||
db.exec('pragma synchronous = OFF;')!
|
||||
} else if sync_mode == .full {
|
||||
db.exec('pragma synchronous = FULL;')
|
||||
db.exec('pragma synchronous = FULL;')!
|
||||
} else {
|
||||
db.exec('pragma synchronous = NORMAL;')
|
||||
db.exec('pragma synchronous = NORMAL;')!
|
||||
}
|
||||
}
|
||||
|
||||
// journal_mode controls how the journal file is stored and processed.
|
||||
// If the command fails to execute an error is returned
|
||||
// .off: No journal record is kept. (fastest)
|
||||
// .memory: Journal record is held in memory, rather than on disk.
|
||||
// .delete: At the conclusion of a transaction, journal file is deleted.
|
||||
// .truncate: Journal file is truncated to a length of zero bytes.
|
||||
// .persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
|
||||
pub fn (db &DB) journal_mode(journal_mode JournalMode) {
|
||||
pub fn (db &DB) journal_mode(journal_mode JournalMode) ! {
|
||||
if journal_mode == .off {
|
||||
db.exec('pragma journal_mode = OFF;')
|
||||
db.exec('pragma journal_mode = OFF;')!
|
||||
} else if journal_mode == .delete {
|
||||
db.exec('pragma journal_mode = DELETE;')
|
||||
db.exec('pragma journal_mode = DELETE;')!
|
||||
} else if journal_mode == .truncate {
|
||||
db.exec('pragma journal_mode = TRUNCATE;')
|
||||
db.exec('pragma journal_mode = TRUNCATE;')!
|
||||
} else if journal_mode == .persist {
|
||||
db.exec('pragma journal_mode = PERSIST;')
|
||||
db.exec('pragma journal_mode = PERSIST;')!
|
||||
} else if journal_mode == .memory {
|
||||
db.exec('pragma journal_mode = MEMORY;')
|
||||
db.exec('pragma journal_mode = MEMORY;')!
|
||||
} else {
|
||||
db.exec('pragma journal_mode = MEMORY;')
|
||||
db.exec('pragma journal_mode = MEMORY;')!
|
||||
}
|
||||
}
|
||||
|
@ -103,11 +103,10 @@ fn test_sqlite_orm() {
|
||||
create table TestCustomSqlType
|
||||
}!
|
||||
|
||||
mut result_custom_sql, mut exec_custom_code := db.exec('
|
||||
mut result_custom_sql := db.exec('
|
||||
pragma table_info(TestCustomSqlType);
|
||||
')
|
||||
')!
|
||||
|
||||
assert exec_custom_code == 101
|
||||
mut table_info_types_results := []string{}
|
||||
information_schema_custom_sql := ['INTEGER', 'INTEGER', 'TEXT', 'REAL', 'NUMERIC', 'TEXT',
|
||||
'INTEGER', 'INTEGER']
|
||||
@ -128,11 +127,10 @@ fn test_sqlite_orm() {
|
||||
create table TestDefaultAtribute
|
||||
}!
|
||||
|
||||
mut result_default_sql, mut code := db.exec('
|
||||
mut result_default_sql := db.exec('
|
||||
pragma table_info(TestDefaultAtribute);
|
||||
')
|
||||
')!
|
||||
|
||||
assert code == 101
|
||||
mut information_schema_data_types_results := []string{}
|
||||
information_schema_default_sql := ['', '', 'CURRENT_TIME', 'CURRENT_DATE', 'CURRENT_TIMESTAMP']
|
||||
|
||||
@ -176,7 +174,7 @@ fn test_get_affected_rows_count() {
|
||||
db.exec('create table EntityToTest(
|
||||
id integer not null constraint tbl_pk primary key,
|
||||
smth integer
|
||||
);')
|
||||
);')!
|
||||
|
||||
fst := EntityToTest{
|
||||
id: 1
|
||||
|
@ -35,49 +35,48 @@ fn test_sqlite() {
|
||||
}
|
||||
mut db := sqlite.connect(':memory:') or { panic(err) }
|
||||
assert db.is_open
|
||||
db.exec('drop table if exists users')
|
||||
db.exec("create table users (id integer primary key, name text default '');")
|
||||
db.exec("insert into users (name) values ('Sam')")
|
||||
db.exec('drop table if exists users')!
|
||||
db.exec("create table users (id integer primary key, name text default '');")!
|
||||
db.exec("insert into users (name) values ('Sam')")!
|
||||
assert db.last_insert_rowid() == 1
|
||||
assert db.get_affected_rows_count() == 1
|
||||
db.exec("insert into users (name) values ('Peter')")
|
||||
db.exec("insert into users (name) values ('Peter')")!
|
||||
assert db.last_insert_rowid() == 2
|
||||
db.exec("insert into users (name) values ('Kate')")
|
||||
db.exec("insert into users (name) values ('Kate')")!
|
||||
assert db.last_insert_rowid() == 3
|
||||
db.exec_param('insert into users (name) values (?)', 'Tom')!
|
||||
assert db.last_insert_rowid() == 4
|
||||
nr_users := db.q_int('select count(*) from users')
|
||||
nr_users := db.q_int('select count(*) from users')!
|
||||
assert nr_users == 4
|
||||
name := db.q_string('select name from users where id = 1')
|
||||
name := db.q_string('select name from users where id = 1')!
|
||||
assert name == 'Sam'
|
||||
username := db.exec_param('select name from users where id = ?', '1')!
|
||||
assert username[0].vals[0] == 'Sam'
|
||||
|
||||
// this insert will be rejected due to duplicated id
|
||||
db.exec("insert into users (id,name) values (1,'Sam')")
|
||||
db.exec("insert into users (id,name) values (1,'Sam')")!
|
||||
assert db.get_affected_rows_count() == 0
|
||||
|
||||
users, mut code := db.exec('select * from users')
|
||||
users := db.exec('select * from users')!
|
||||
assert users.len == 4
|
||||
assert code == 101
|
||||
code = db.exec_none('vacuum')
|
||||
code := db.exec_none('vacuum')
|
||||
assert code == 101
|
||||
user := db.exec_one('select * from users where id = 3') or { panic(err) }
|
||||
println(user)
|
||||
assert user.vals.len == 2
|
||||
|
||||
db.exec("update users set name='zzzz' where name='qqqq'")
|
||||
db.exec("update users set name='zzzz' where name='qqqq'")!
|
||||
assert db.get_affected_rows_count() == 0
|
||||
|
||||
db.exec("update users set name='Peter1' where name='Peter'")
|
||||
db.exec("update users set name='Peter1' where name='Peter'")!
|
||||
assert db.get_affected_rows_count() == 1
|
||||
db.exec_param_many('update users set name=? where name=?', ['Peter', 'Peter1'])!
|
||||
assert db.get_affected_rows_count() == 1
|
||||
|
||||
db.exec("delete from users where name='qqqq'")
|
||||
db.exec("delete from users where name='qqqq'")!
|
||||
assert db.get_affected_rows_count() == 0
|
||||
|
||||
db.exec("delete from users where name='Sam'")
|
||||
db.exec("delete from users where name='Sam'")!
|
||||
assert db.get_affected_rows_count() == 1
|
||||
|
||||
db.close() or { panic(err) }
|
||||
|
@ -32,17 +32,17 @@ const (
|
||||
|
||||
fn test_search_tag_by_type() {
|
||||
mut dom := parse(html.html)
|
||||
tag := dom.get_tag('body')[0]
|
||||
assert tag.get_tag('div') or { assert false }.attributes['id'] == '1st'
|
||||
assert tag.get_tag_by_attribute('href') or { assert false }.content == 'V'
|
||||
tag := dom.get_tags(GetTagsOptions{'body'})[0]
|
||||
assert tag.get_tag('div')?.attributes['id'] == '1st'
|
||||
assert tag.get_tag_by_attribute('href')?.content == 'V'
|
||||
// TODO: update after improved parsing to not add trailing white space to attribute values
|
||||
assert tag.get_tag_by_attribute_value('id', '3rd') or { assert false }.str() == '<div id="3rd" ></div>'
|
||||
assert tag.get_tag_by_class_name('foo') or { assert false }.attributes['class'] == 'foo bar'
|
||||
assert tag.get_tag_by_attribute_value('id', '3rd')?.str() == '<div id="3rd" ></div>'
|
||||
assert tag.get_tag_by_class_name('foo')?.attributes['class'] == 'foo bar'
|
||||
}
|
||||
|
||||
fn test_search_tags_by_type() {
|
||||
mut dom := parse(html.html)
|
||||
tag := dom.get_tag_by_attribute_value('id', '2nd')[0]
|
||||
tag := dom.get_tags_by_attribute_value('id', '2nd')[0]
|
||||
assert tag.get_tags('div').len == 5
|
||||
assert tag.get_tags_by_attribute('href')[2].content == 'vpm'
|
||||
assert tag.get_tags_by_attribute_value('class', 'bar').len == 3
|
||||
@ -65,7 +65,7 @@ fn generate_temp_html_with_classes() string {
|
||||
|
||||
fn test_search_by_class() {
|
||||
mut dom := parse(generate_temp_html_with_classes())
|
||||
tag := dom.get_tag('body')[0]
|
||||
tag := dom.get_tags(GetTagsOptions{'body'})[0]
|
||||
single_class_tags := tag.get_tags_by_class_name('single')
|
||||
common_class_tags := tag.get_tags_by_class_name('common')
|
||||
complex_class_tags := tag.get_tags_by_class_name('complex-0', 'complex-1', 'complex-2')
|
||||
|
@ -23,7 +23,7 @@ fn test_ensure_db_exists_and_user_table_is_ok() {
|
||||
assert true
|
||||
|
||||
eprintln('> drop pre-existing User table...')
|
||||
db.exec('drop table if exists User')
|
||||
db.exec('drop table if exists User')!
|
||||
|
||||
eprintln('> creating User table...')
|
||||
sql db {
|
||||
|
@ -262,8 +262,7 @@ fn ptr_win_get_error_msg(code u32) voidptr {
|
||||
return buf
|
||||
}
|
||||
C.FormatMessage(os.format_message_allocate_buffer | os.format_message_from_system | os.format_message_ignore_inserts,
|
||||
0, code, C.MAKELANGID(os.lang_neutral, os.sublang_default), voidptr(&buf), 0,
|
||||
0)
|
||||
0, code, 0, voidptr(&buf), 0, 0)
|
||||
return buf
|
||||
}
|
||||
|
||||
|
@ -5,34 +5,34 @@ fn test_custom_format() {
|
||||
assert date.custom_format('YYYY-MM-DD HH:mm') == date.format()
|
||||
assert date.custom_format('MMM') == date.smonth()
|
||||
|
||||
test_str := 'M MM Mo MMM MMMM\nD DD DDD DDDD\nd dd ddd dddd\nYY YYYY a A\nH HH h hh k kk e\nm mm s ss Z ZZ ZZZ\nDo DDDo Q Qo QQ\nN NN w wo ww\nM/D/YYYY N-HH:mm:ss Qo?a'
|
||||
test_str := 'M MM Mo MMM MMMM\nD DD DDD DDDD\nd dd ddd dddd\nYY YYYY a A\nH HH h hh k kk i ii e\nm mm s ss Z ZZ ZZZ\nDo DDDo Q Qo QQ\nN NN w wo ww\nM/D/YYYY N-HH:mm:ss Qo?a'
|
||||
|
||||
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'
|
||||
assert time.parse('2023-08-04 00:00:45')!.custom_format('ii A i a hh A h a') == '00 AM 0 am 12 AM 12 am'
|
||||
assert time.parse('2023-08-04 01:00:45')!.custom_format('ii A i a hh A h a') == '01 AM 1 am 01 AM 1 am'
|
||||
assert time.parse('2023-08-04 02:00:45')!.custom_format('ii A i a hh A h a') == '02 AM 2 am 02 AM 2 am'
|
||||
assert time.parse('2023-08-04 03:00:45')!.custom_format('ii A i a hh A h a') == '03 AM 3 am 03 AM 3 am'
|
||||
assert time.parse('2023-08-04 04:00:45')!.custom_format('ii A i a hh A h a') == '04 AM 4 am 04 AM 4 am'
|
||||
assert time.parse('2023-08-04 05:00:45')!.custom_format('ii A i a hh A h a') == '05 AM 5 am 05 AM 5 am'
|
||||
assert time.parse('2023-08-04 06:00:45')!.custom_format('ii A i a hh A h a') == '06 AM 6 am 06 AM 6 am'
|
||||
assert time.parse('2023-08-04 07:00:45')!.custom_format('ii A i a hh A h a') == '07 AM 7 am 07 AM 7 am'
|
||||
assert time.parse('2023-08-04 08:00:45')!.custom_format('ii A i a hh A h a') == '08 AM 8 am 08 AM 8 am'
|
||||
assert time.parse('2023-08-04 09:00:45')!.custom_format('ii A i a hh A h a') == '09 AM 9 am 09 AM 9 am'
|
||||
assert time.parse('2023-08-04 10:00:45')!.custom_format('ii A i a hh A h a') == '10 AM 10 am 10 AM 10 am'
|
||||
assert time.parse('2023-08-04 11:00:45')!.custom_format('ii A i a hh A h a') == '11 AM 11 am 11 AM 11 am'
|
||||
assert time.parse('2023-08-04 12:00:45')!.custom_format('ii A i a hh A h a') == '12 PM 12 pm 12 PM 12 pm'
|
||||
assert time.parse('2023-08-04 13:00:45')!.custom_format('ii A i a hh A h a') == '01 PM 1 pm 01 PM 1 pm'
|
||||
assert time.parse('2023-08-04 14:00:45')!.custom_format('ii A i a hh A h a') == '02 PM 2 pm 02 PM 2 pm'
|
||||
assert time.parse('2023-08-04 15:00:45')!.custom_format('ii A i a hh A h a') == '03 PM 3 pm 03 PM 3 pm'
|
||||
assert time.parse('2023-08-04 16:00:45')!.custom_format('ii A i a hh A h a') == '04 PM 4 pm 04 PM 4 pm'
|
||||
assert time.parse('2023-08-04 17:00:45')!.custom_format('ii A i a hh A h a') == '05 PM 5 pm 05 PM 5 pm'
|
||||
assert time.parse('2023-08-04 18:00:45')!.custom_format('ii A i a hh A h a') == '06 PM 6 pm 06 PM 6 pm'
|
||||
assert time.parse('2023-08-04 19:00:45')!.custom_format('ii A i a hh A h a') == '07 PM 7 pm 07 PM 7 pm'
|
||||
assert time.parse('2023-08-04 20:00:45')!.custom_format('ii A i a hh A h a') == '08 PM 8 pm 08 PM 8 pm'
|
||||
assert time.parse('2023-08-04 21:00:45')!.custom_format('ii A i a hh A h a') == '09 PM 9 pm 09 PM 9 pm'
|
||||
assert time.parse('2023-08-04 22:00:45')!.custom_format('ii A i a hh A h a') == '10 PM 10 pm 10 PM 10 pm'
|
||||
assert time.parse('2023-08-04 23:00:45')!.custom_format('ii A i a hh A h a') == '11 PM 11 pm 11 PM 11 pm'
|
||||
}
|
||||
|
@ -20,14 +20,6 @@ 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.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)
|
||||
// RFC3339 is an Internet profile, based on the ISO 8601 standard for for representation of dates and times using the Gregorian calendar.
|
||||
// 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.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.nanosecond / 1_000):06d}'
|
||||
@ -38,6 +30,20 @@ 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}'
|
||||
}
|
||||
|
||||
// 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)
|
||||
// RFC3339 is an Internet profile, based on the ISO 8601 standard for for representation of dates and times using the Gregorian calendar.
|
||||
// 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.nanosecond / 1_000_000):03d}Z'
|
||||
}
|
||||
|
||||
// format_rfc3339_nano returns a date string in "YYYY-MM-DDTHH:mm:ss.123456789Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
|
||||
pub fn (t Time) format_rfc3339_nano() 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.nanosecond):09d}Z'
|
||||
}
|
||||
|
||||
// hhmm returns a date string in "HH:mm" format (24h).
|
||||
pub fn (t Time) hhmm() string {
|
||||
return '${t.hour:02d}:${t.minute:02d}'
|
||||
@ -90,8 +96,8 @@ fn ordinal_suffix(n int) string {
|
||||
}
|
||||
|
||||
const (
|
||||
tokens_2 = ['MM', 'Mo', 'DD', 'Do', 'YY', 'ss', 'kk', 'NN', 'mm', 'hh', 'HH', 'ZZ', 'dd', 'Qo',
|
||||
'QQ', 'wo', 'ww']
|
||||
tokens_2 = ['MM', 'Mo', 'DD', 'Do', 'YY', 'ss', 'kk', 'NN', 'mm', 'hh', 'HH', 'ii', 'ZZ', 'dd',
|
||||
'Qo', 'QQ', 'wo', 'ww']
|
||||
tokens_3 = ['MMM', 'DDD', 'ZZZ', 'ddd']
|
||||
tokens_4 = ['MMMM', 'DDDD', 'DDDo', 'dddd', 'YYYY']
|
||||
)
|
||||
@ -132,6 +138,8 @@ const (
|
||||
// | | HH | 00 01 ... 22 23 |
|
||||
// | | h | 1 2 ... 11 12 |
|
||||
// | | hh | 01 02 ... 11 12 |
|
||||
// | | i | 0 1 ... 11 12 1 ... 11 |
|
||||
// | | ii | 00 01 ... 11 12 01 ... 11 |
|
||||
// | | k | 1 2 ... 23 24 |
|
||||
// | | kk | 01 02 ... 23 24 |
|
||||
// | **Minute** | m | 0 1 ... 58 59 |
|
||||
@ -226,10 +234,18 @@ pub fn (t Time) custom_format(s string) string {
|
||||
sb.write_string('${t.hour:02}')
|
||||
}
|
||||
'h' {
|
||||
h := if t.hour > 12 { t.hour - 12 } else { t.hour }
|
||||
h := (t.hour + 11) % 12 + 1
|
||||
sb.write_string(h.str())
|
||||
}
|
||||
'hh' {
|
||||
h := (t.hour + 11) % 12 + 1
|
||||
sb.write_string('${h:02}')
|
||||
}
|
||||
'i' {
|
||||
h := if t.hour > 12 { t.hour - 12 } else { t.hour }
|
||||
sb.write_string(h.str())
|
||||
}
|
||||
'ii' {
|
||||
h := if t.hour > 12 { t.hour - 12 } else { t.hour }
|
||||
sb.write_string('${h:02}')
|
||||
}
|
||||
|
@ -89,6 +89,13 @@ fn test_format_rfc3339() {
|
||||
assert res.contains('T')
|
||||
}
|
||||
|
||||
fn test_format_rfc3339_nano() {
|
||||
res := time_to_test.format_rfc3339_nano()
|
||||
assert res.ends_with('23:42.123456789Z')
|
||||
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)
|
||||
}
|
||||
|
@ -526,6 +526,9 @@ fn (mut c Checker) alias_type_decl(node ast.AliasTypeDecl) {
|
||||
// type Sum = int | Alias
|
||||
// type Alias = Sum
|
||||
}
|
||||
.none_ {
|
||||
c.error('cannot create a type alias of `none` as it is a value', node.type_pos)
|
||||
}
|
||||
// The rest of the parent symbol kinds are also allowed, since they are either primitive types,
|
||||
// that in turn do not allow recursion, or are abstract enough so that they can not be checked at comptime:
|
||||
else {}
|
||||
@ -1143,6 +1146,8 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ
|
||||
c.check_expr_opt_call(expr.expr, ret_type)
|
||||
} else if expr is ast.AsCast {
|
||||
c.check_expr_opt_call(expr.expr, ret_type)
|
||||
} else if expr is ast.ParExpr {
|
||||
c.check_expr_opt_call(expr.expr, ret_type)
|
||||
}
|
||||
return ret_type
|
||||
}
|
||||
@ -1774,9 +1779,6 @@ fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
|
||||
useen << uval
|
||||
}
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
dump(field.expr)
|
||||
}
|
||||
ast.InfixExpr {
|
||||
// Handle `enum Foo { x = 1 + 2 }`
|
||||
c.infix_expr(mut field.expr)
|
||||
|
@ -490,6 +490,9 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
|
||||
c.error('unknown function: ${node.name}', node.pos)
|
||||
}
|
||||
}
|
||||
// If the left expr has an or_block, it needs to be checked for legal or_block statement.
|
||||
return_type := c.expr(mut node.left)
|
||||
c.check_expr_opt_call(node.left, return_type)
|
||||
// TODO merge logic from method_call and fn_call
|
||||
// First check everything that applies to both fns and methods
|
||||
old_inside_fn_arg := c.inside_fn_arg
|
||||
|
@ -77,7 +77,7 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
||||
if sym.kind == .function {
|
||||
if !field.typ.has_flag(.option) && !field.has_default_expr
|
||||
&& field.attrs.filter(it.name == 'required').len == 0 {
|
||||
error_msg := 'uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)'
|
||||
error_msg := 'uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)'
|
||||
c.note(error_msg, field.pos)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
vlib/v/checker/tests/fn_check_for_matching_option_result_in_fields.vv:2:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
|
||||
vlib/v/checker/tests/fn_check_for_matching_option_result_in_fields.vv:2:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
|
||||
1 | struct Abc {
|
||||
2 | f fn (voidptr)
|
||||
| ~~~~~~~~~~~~~~
|
||||
|
@ -1,10 +1,17 @@
|
||||
vlib/v/checker/tests/generics_struct_init_err.vv:14:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
|
||||
vlib/v/checker/tests/generics_struct_init_err.vv:14:2: notice: uninitialized `fn` struct fields are not allowed, since they can result in segfaults; use `?fn` or `[required]` or initialize the field with `=` (if you absolutely want to have unsafe function pointers, use `= unsafe { nil }`)
|
||||
12 |
|
||||
13 | struct FnHolder2[T] {
|
||||
14 | func fn (int) int
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
15 | }
|
||||
16 |
|
||||
vlib/v/checker/tests/generics_struct_init_err.vv:58:8: error: cannot initialize builtin type `FnHolder1[neg]`
|
||||
56 | ret = holder_call_12(neg, 3)
|
||||
57 | assert ret == -3
|
||||
58 | ret = FnHolder1{neg}.call(4)
|
||||
| ~~~~~~~~~~~~~~
|
||||
59 | assert ret == -4
|
||||
60 |
|
||||
vlib/v/checker/tests/generics_struct_init_err.vv:67:8: error: could not infer generic type `T` in generic struct `FnHolder2[T]`
|
||||
65 | ret = holder_call_22(neg, 5)
|
||||
66 | assert ret == -5
|
||||
|
@ -1,6 +1,6 @@
|
||||
vlib/v/checker/tests/option_type_call_err.vv:4:5: error: Result type cannot be called directly
|
||||
2 |
|
||||
vlib/v/checker/tests/option_type_call_err.vv:4:5: error: os.ls() returns a Result, so it should have either an `or {}` block, or `!` at the end
|
||||
2 |
|
||||
3 | fn main() {
|
||||
4 | os.ls('.').filter(it.ends_with('.v')) or { return }
|
||||
| ~~~~~~~
|
||||
5 | }
|
||||
5 | }
|
||||
|
27
vlib/v/checker/tests/or_block_check_err.out
Normal file
27
vlib/v/checker/tests/or_block_check_err.out
Normal file
@ -0,0 +1,27 @@
|
||||
vlib/v/checker/tests/or_block_check_err.vv:6:36: error: assignment requires a non empty `or {}` block
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
||||
| ~~~~~
|
||||
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
||||
8 |
|
||||
vlib/v/checker/tests/or_block_check_err.vv:7:37: error: assignment requires a non empty `or {}` block
|
||||
5 | fn main() {
|
||||
6 | _ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
||||
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
||||
| ~~~~~
|
||||
8 |
|
||||
9 | _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b')
|
||||
vlib/v/checker/tests/or_block_check_err.vv:9:41: error: `or` block must provide a default value of type `string`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1)
|
||||
7 | _ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
||||
8 |
|
||||
9 | _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b')
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
10 | _ = (callexpr_with_or_block_call() or { eprintln('error') }).replace('a', 'b')
|
||||
11 | }
|
||||
vlib/v/checker/tests/or_block_check_err.vv:10:42: error: `or` block must provide a default value of type `string`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1)
|
||||
8 |
|
||||
9 | _ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b')
|
||||
10 | _ = (callexpr_with_or_block_call() or { eprintln('error') }).replace('a', 'b')
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
11 | }
|
11
vlib/v/checker/tests/or_block_check_err.vv
Normal file
11
vlib/v/checker/tests/or_block_check_err.vv
Normal file
@ -0,0 +1,11 @@
|
||||
fn callexpr_with_or_block_call() !string {
|
||||
return error('')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
_ = callexpr_with_or_block_call() or {}.replace('a', 'b')
|
||||
_ = (callexpr_with_or_block_call() or {}).replace('a', 'b')
|
||||
|
||||
_ = callexpr_with_or_block_call() or { eprintln('error') }.replace('a', 'b')
|
||||
_ = (callexpr_with_or_block_call() or { eprintln('error') }).replace('a', 'b')
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
vlib/v/checker/tests/result_type_call_err.vv:12:2: error: Result type cannot be called directly
|
||||
10 |
|
||||
vlib/v/checker/tests/result_type_call_err.vv:12:2: error: new_foo() returns a Result, so it should have either an `or {}` block, or `!` at the end
|
||||
10 |
|
||||
11 | fn main() {
|
||||
12 | new_foo().foo()
|
||||
| ~~~~~~~~~
|
||||
|
3
vlib/v/checker/tests/type_alias_none_parent_type_err.out
Normal file
3
vlib/v/checker/tests/type_alias_none_parent_type_err.out
Normal file
@ -0,0 +1,3 @@
|
||||
vlib/v/checker/tests/type_alias_none_parent_type_err.vv:1:13: error: cannot create a type alias of `none` as it is a value
|
||||
1 | type None = none
|
||||
| ~~~~
|
1
vlib/v/checker/tests/type_alias_none_parent_type_err.vv
Normal file
1
vlib/v/checker/tests/type_alias_none_parent_type_err.vv
Normal file
@ -0,0 +1 @@
|
||||
type None = none
|
@ -38,6 +38,7 @@ pub mut:
|
||||
is_inter_start bool // for hacky string interpolation TODO simplify
|
||||
is_inter_end bool
|
||||
is_enclosed_inter bool
|
||||
is_nested_enclosed_inter bool
|
||||
line_comment string
|
||||
last_lt int = -1 // position of latest <
|
||||
is_started bool
|
||||
@ -648,7 +649,7 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||
}
|
||||
// End of $var, start next string
|
||||
if s.is_inter_end {
|
||||
if s.text[s.pos] == s.quote {
|
||||
if s.text[s.pos] == s.quote || (s.text[s.pos] == s.inter_quote && s.is_enclosed_inter) {
|
||||
s.is_inter_end = false
|
||||
return s.new_token(.string, '', 1)
|
||||
}
|
||||
@ -818,7 +819,7 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||
return s.new_token(.lcbr, '', 1)
|
||||
}
|
||||
`$` {
|
||||
if s.is_inside_string {
|
||||
if s.is_inside_string || s.is_enclosed_inter {
|
||||
return s.new_token(.str_dollar, '', 1)
|
||||
} else {
|
||||
return s.new_token(.dollar, '', 1)
|
||||
@ -827,7 +828,7 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||
`}` {
|
||||
// s = `hello $name !`
|
||||
// s = `hello ${name} !`
|
||||
if s.is_enclosed_inter && s.inter_cbr_count == 0 {
|
||||
if (s.is_enclosed_inter || s.is_nested_enclosed_inter) && s.inter_cbr_count == 0 {
|
||||
if s.pos < s.text.len - 1 {
|
||||
s.pos++
|
||||
} else {
|
||||
@ -835,10 +836,18 @@ fn (mut s Scanner) text_scan() token.Token {
|
||||
}
|
||||
if s.text[s.pos] == s.quote {
|
||||
s.is_inside_string = false
|
||||
s.is_enclosed_inter = false
|
||||
if s.is_nested_enclosed_inter {
|
||||
s.is_nested_enclosed_inter = false
|
||||
} else {
|
||||
s.is_enclosed_inter = false
|
||||
}
|
||||
return s.new_token(.string, '', 1)
|
||||
}
|
||||
s.is_enclosed_inter = false
|
||||
if s.is_nested_enclosed_inter {
|
||||
s.is_nested_enclosed_inter = false
|
||||
} else {
|
||||
s.is_enclosed_inter = false
|
||||
}
|
||||
s.just_closed_inter = true
|
||||
ident_string := s.ident_string()
|
||||
return s.new_token(.string, ident_string, ident_string.len + 2) // + two quotes
|
||||
@ -1229,7 +1238,11 @@ fn (mut s Scanner) ident_string() string {
|
||||
if prevc == `$` && c == `{` && !is_raw
|
||||
&& s.count_symbol_before(s.pos - 2, scanner.backslash) % 2 == 0 {
|
||||
s.is_inside_string = true
|
||||
s.is_enclosed_inter = true
|
||||
if s.is_enclosed_inter {
|
||||
s.is_nested_enclosed_inter = true
|
||||
} else {
|
||||
s.is_enclosed_inter = true
|
||||
}
|
||||
// so that s.pos points to $ at the next step
|
||||
s.pos -= 2
|
||||
break
|
||||
@ -1472,17 +1485,23 @@ fn (mut s Scanner) ident_char() string {
|
||||
u := c.runes()
|
||||
if u.len != 1 {
|
||||
if escaped_hex || escaped_unicode {
|
||||
s.error('invalid character literal `${orig}` => `${c}` (${u}) (escape sequence did not refer to a singular rune)')
|
||||
s.error_with_pos('invalid character literal `${orig}` => `${c}` (${u}) (escape sequence did not refer to a singular rune)',
|
||||
lspos)
|
||||
} else if u.len == 0 {
|
||||
s.add_error_detail_with_pos('use quotes for strings, backticks for characters',
|
||||
lspos)
|
||||
s.error('invalid empty character literal `${orig}`')
|
||||
s.error_with_pos('invalid empty character literal `${orig}`', lspos)
|
||||
} else {
|
||||
s.add_error_detail_with_pos('use quotes for strings, backticks for characters',
|
||||
lspos)
|
||||
s.error('invalid character literal `${orig}` => `${c}` (${u}) (more than one character)')
|
||||
s.error_with_pos('invalid character literal `${orig}` => `${c}` (${u}) (more than one character)',
|
||||
lspos)
|
||||
}
|
||||
}
|
||||
} else if c == '\n' {
|
||||
s.add_error_detail_with_pos('use quotes for strings, backticks for characters',
|
||||
lspos)
|
||||
s.error_with_pos('invalid character literal, use \`\\n\` instead', lspos)
|
||||
}
|
||||
// Escapes a `'` character
|
||||
if c == "'" {
|
||||
@ -1566,15 +1585,19 @@ fn (mut s Scanner) eat_details() string {
|
||||
}
|
||||
|
||||
pub fn (mut s Scanner) warn(msg string) {
|
||||
if s.pref.warns_are_errors {
|
||||
s.error(msg)
|
||||
return
|
||||
}
|
||||
pos := token.Pos{
|
||||
line_nr: s.line_nr
|
||||
pos: s.pos
|
||||
col: s.current_column() - 1
|
||||
}
|
||||
s.warn_with_pos(msg, pos)
|
||||
}
|
||||
|
||||
pub fn (mut s Scanner) warn_with_pos(msg string, pos token.Pos) {
|
||||
if s.pref.warns_are_errors {
|
||||
s.error_with_pos(msg, pos)
|
||||
return
|
||||
}
|
||||
details := s.eat_details()
|
||||
if s.pref.output_mode == .stdout && !s.pref.check_only {
|
||||
util.show_compiler_message('warning:',
|
||||
@ -1604,6 +1627,10 @@ pub fn (mut s Scanner) error(msg string) {
|
||||
pos: s.pos
|
||||
col: s.current_column() - 1
|
||||
}
|
||||
s.error_with_pos(msg, pos)
|
||||
}
|
||||
|
||||
pub fn (mut s Scanner) error_with_pos(msg string, pos token.Pos) {
|
||||
details := s.eat_details()
|
||||
if s.pref.output_mode == .stdout && !s.pref.check_only {
|
||||
util.show_compiler_message('error:',
|
||||
|
@ -1,10 +1,10 @@
|
||||
vlib/v/scanner/tests/empty_character_literal_err.vv:2:8: error: invalid empty character literal ``
|
||||
vlib/v/scanner/tests/empty_character_literal_err.vv:2:7: error: invalid empty character literal ``
|
||||
1 | fn main() {
|
||||
2 | a := ``
|
||||
| ^
|
||||
| ^
|
||||
3 | println(a)
|
||||
4 | }
|
||||
Details:
|
||||
Details:
|
||||
vlib/v/scanner/tests/empty_character_literal_err.vv:2:7: details: use quotes for strings, backticks for characters
|
||||
1 | fn main() {
|
||||
2 | a := ``
|
||||
|
13
vlib/v/scanner/tests/newline_character_literal_err.out
Normal file
13
vlib/v/scanner/tests/newline_character_literal_err.out
Normal file
@ -0,0 +1,13 @@
|
||||
vlib/v/scanner/tests/newline_character_literal_err.vv:2:7: error: invalid character literal, use `\n` instead
|
||||
1 | fn main() {
|
||||
2 | a := `
|
||||
| ^
|
||||
3 | `
|
||||
4 | println(a)
|
||||
Details:
|
||||
vlib/v/scanner/tests/newline_character_literal_err.vv:2:7: details: use quotes for strings, backticks for characters
|
||||
1 | fn main() {
|
||||
2 | a := `
|
||||
| ^
|
||||
3 | `
|
||||
4 | println(a)
|
5
vlib/v/scanner/tests/newline_character_literal_err.vv
Normal file
5
vlib/v/scanner/tests/newline_character_literal_err.vv
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
a := `
|
||||
`
|
||||
println(a)
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:5] 2 + 2: 4
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:6] 2 * 3 + 50 / 5: 16
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:7] 3.14 + 0.1: 3.24
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:8] 'abc' + 'a': abca
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:9] 'a' + 'b' + 'c': abc
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:10] true || (false && true): true
|
||||
[vlib/v/slow_tests/inout/dump_expressions_with_literals.vv:11] 2 == 4: false
|
12
vlib/v/slow_tests/inout/dump_expressions_with_literals.vv
Normal file
12
vlib/v/slow_tests/inout/dump_expressions_with_literals.vv
Normal file
@ -0,0 +1,12 @@
|
||||
// Note: dump expressions should not get optimised out by the transformer stage,
|
||||
// even though they could normally, when they are composed of literals, i.e.
|
||||
// the value of the expression is known at compile time.
|
||||
fn main() {
|
||||
dump(2 + 2)
|
||||
dump(2 * 3 + 50 / 5)
|
||||
dump(3.14 + 0.1)
|
||||
dump('abc' + 'a')
|
||||
dump('a' + 'b' + 'c')
|
||||
dump(true || (false && true))
|
||||
dump(2 == 4)
|
||||
}
|
38
vlib/v/tests/string_interpolation_with_inner_quotes_test.v
Normal file
38
vlib/v/tests/string_interpolation_with_inner_quotes_test.v
Normal file
@ -0,0 +1,38 @@
|
||||
fn f(x int, s string) string {
|
||||
return 'label ${s}: ${x}'
|
||||
}
|
||||
|
||||
// vfmt off
|
||||
fn test_string_interp_with_inner_quotes() {
|
||||
x := 'hi'
|
||||
println('abc ${f(123, 'def')} xyz')
|
||||
assert 'abc ${f(123, 'def')} xyz' == 'abc label def: 123 xyz'
|
||||
|
||||
println('abc ${f(123, "def")} xyz')
|
||||
assert 'abc ${f(123, "def")} xyz' == 'abc label def: 123 xyz'
|
||||
|
||||
println("abc ${f(123, 'def')} xyz")
|
||||
assert "abc ${f(123, 'def')} xyz" == 'abc label def: 123 xyz'
|
||||
|
||||
println("abc ${f(123, "def")} xyz")
|
||||
assert "abc ${f(123, "def")} xyz" == 'abc label def: 123 xyz'
|
||||
|
||||
println("abc ${f(123, "$x $x")} xyz")
|
||||
assert "abc ${f(123, "$x $x")} xyz" == 'abc label hi hi: 123 xyz'
|
||||
|
||||
println('abc ${f(123, '$x $x')} xyz')
|
||||
assert 'abc ${f(123, '$x $x')} xyz' == 'abc label hi hi: 123 xyz'
|
||||
|
||||
println('abc ${f(123, "$x $x")} xyz')
|
||||
assert 'abc ${f(123, "$x $x")} xyz' == 'abc label hi hi: 123 xyz'
|
||||
|
||||
println("abc ${f(123, '$x $x')} xyz")
|
||||
assert "abc ${f(123, '$x $x')} xyz" == 'abc label hi hi: 123 xyz'
|
||||
|
||||
println("abc ${f(123, "${x} ${x}")} xyz")
|
||||
assert "abc ${f(123, "${x} ${x}")} xyz" == 'abc label hi hi: 123 xyz'
|
||||
|
||||
println('abc ${f(123, '${x} ${x}')} xyz')
|
||||
assert 'abc ${f(123, '${x} ${x}')} xyz' == 'abc label hi hi: 123 xyz'
|
||||
}
|
||||
// vfmt on
|
@ -10,7 +10,8 @@ pub mut:
|
||||
index &IndexState
|
||||
table &ast.Table = unsafe { nil }
|
||||
mut:
|
||||
is_assert bool
|
||||
is_assert bool
|
||||
inside_dump bool
|
||||
}
|
||||
|
||||
pub fn new_transformer(pref_ &pref.Preferences) &Transformer {
|
||||
@ -513,6 +514,9 @@ pub fn (mut t Transformer) interface_decl(mut node ast.InterfaceDecl) ast.Stmt {
|
||||
}
|
||||
|
||||
pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
|
||||
if t.inside_dump {
|
||||
return node
|
||||
}
|
||||
match mut node {
|
||||
ast.AnonFn {
|
||||
node.decl = t.stmt(mut node.decl) as ast.FnDecl
|
||||
@ -563,7 +567,10 @@ pub fn (mut t Transformer) expr(mut node ast.Expr) ast.Expr {
|
||||
}
|
||||
}
|
||||
ast.DumpExpr {
|
||||
old_inside_dump := t.inside_dump
|
||||
t.inside_dump = true
|
||||
node.expr = t.expr(mut node.expr)
|
||||
t.inside_dump = old_inside_dump
|
||||
}
|
||||
ast.GoExpr {
|
||||
node.call_expr = t.expr(mut node.call_expr) as ast.CallExpr
|
||||
|
@ -152,7 +152,7 @@ fn mod_path_to_full_name(pref_ &pref.Preferences, mod string, path string) !stri
|
||||
}
|
||||
if last_v_mod > -1 {
|
||||
mod_full_name := try_path_parts[last_v_mod..].join('.')
|
||||
return mod_full_name
|
||||
return if mod_full_name.len < mod.len { mod } else { mod_full_name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user