this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

working on database

+1063 -63
+40 -2
src/core/types.odin
··· 1 1 package core 2 2 3 - Song :: struct { 4 - title: string, 3 + Track_Id :: distinct string 4 + Artist_Id :: distinct string 5 + Album_Id :: distinct string 6 + 7 + 8 + Track :: struct { 9 + id: Track_Id, 10 + title: string, 11 + track_number: u8, 12 + mb_id: Maybe(string), // MUSICBRAINZ_TRACKID, 13 + album_id: Album_Id, 14 + } 15 + 16 + Artist :: struct { 17 + id: Artist_Id, 18 + name: string, 19 + acoust_id: Maybe(string), 20 + mb_id: Maybe(string), // MUSICBRAINZ_ARTISTID, 21 + } 22 + 23 + Album :: struct { 24 + id: Album_Id, 25 + title: string, 26 + mb_id: Maybe(string), // MUSICBRAINZ_ALBUMID, 27 + mb_rg_id: Maybe(string), // MUSICBRAINZ_RELEASEGROUPID, 28 + } 29 + 30 + 31 + AlbumFull :: struct { 32 + album: Album, 33 + artists: []Artist, 34 + tracks: []struct { 35 + track: Track, 36 + artists: []Artist, 37 + }, 38 + } 39 + 40 + AlbumShort :: struct { 41 + album: Album, 42 + artists: []Artist, 5 43 }
+89
src/db/migrate.odin
··· 1 + package db 2 + 3 + import "core:fmt" 4 + import "core:mem" 5 + import "core:os" 6 + import "core:path/filepath" 7 + import "core:slice" 8 + import "core:strings" 9 + 10 + import sqlite "../../vendor/sqlite" 11 + import sa "../../vendor/sqlite/addons" 12 + 13 + DB_URL :: "oudio.db" 14 + 15 + main :: proc() { 16 + 17 + alloc := context.temp_allocator 18 + 19 + migration_dir, err := filepath.join({#directory, "migrations"}, alloc) 20 + 21 + assert(err == nil, "Unable to resolve migrations folder path") 22 + 23 + dir_entries, err2 := os.read_all_directory_by_path(migration_dir, alloc) 24 + defer os.file_info_slice_delete(dir_entries, alloc) 25 + 26 + assert(err2 == nil, "Unable to find migration list from folder") 27 + 28 + only_sql := slice.filter(dir_entries, proc(x: os.File_Info) -> bool { 29 + return strings.has_suffix(x.fullpath, ".sql") 30 + }) 31 + 32 + slice.sort_by(only_sql, proc(a, b: os.File_Info) -> bool { 33 + return strings.compare(a.fullpath, b.fullpath) < 0 34 + }) 35 + 36 + fmt.printfln("Entries: %#v", only_sql) 37 + 38 + // Apply 39 + 40 + track: mem.Tracking_Allocator 41 + mem.tracking_allocator_init(&track, context.allocator) 42 + 43 + defer { 44 + if len(track.allocation_map) > 0 { 45 + fmt.eprintf("=== %v allocations not freed: ===\n", len(track.allocation_map)) 46 + for _, entry in track.allocation_map { 47 + fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location) 48 + } 49 + } 50 + if len(track.bad_free_array) > 0 { 51 + fmt.eprintf("=== %v incorrect frees: ===\n", len(track.bad_free_array)) 52 + for entry in track.bad_free_array { 53 + fmt.eprintf("- %p @ %v\n", entry.memory, entry.location) 54 + } 55 + } 56 + mem.tracking_allocator_destroy(&track) 57 + } 58 + 59 + db: ^sqlite.Connection 60 + 61 + if rc := sqlite.open(DB_URL, &db); rc != .Ok { 62 + fmt.panicf("failed to open database. result code {}", rc) 63 + } 64 + fmt.printfln("connected to database") 65 + 66 + defer { 67 + sqlite.close(db) 68 + fmt.printfln("\nconnection closed") 69 + } 70 + 71 + for migration in only_sql { 72 + apply(db, migration.fullpath) 73 + } 74 + } 75 + 76 + apply :: proc(db: ^sqlite.Connection, path: string, allocator := context.allocator) { 77 + 78 + data, err := os.read_entire_file_from_path(path, allocator) 79 + assert(err == nil, "Unable to apply migration") 80 + defer delete(data, allocator) 81 + 82 + text := string(data) 83 + 84 + fmt.printfln("Migration: %#v", text) 85 + 86 + 87 + rc := sa.execute(db, text) 88 + assert(rc == .Ok) 89 + }
+49
src/db/migrations/01-init.sql
··· 1 + CREATE TABLE migrations ( 2 + id INTEGER PRIMARY KEY, 3 + name TEXT NOT NULL, 4 + applied_at DATE 5 + ); 6 + 7 + CREATE TABLE album ( 8 + id TEXT PRIMARY KEY, 9 + title TEXT NOT NULL, 10 + mb_id TEXT, 11 + mb_rg_id TEXT 12 + ); 13 + 14 + CREATE TABLE artist ( 15 + id TEXT PRIMARY KEY, 16 + name TEXT NOT NULL UNIQUE 17 + ); 18 + 19 + CREATE TABLE track ( 20 + id TEXT PRIMARY KEY, 21 + title TEXT NOT NULL, 22 + track_number INTEGER NOT NULL DEFAULT 0, 23 + mb_id TEXT, 24 + album_id TEXT NOT NULL, 25 + 26 + FOREIGN KEY(album_id) REFERENCES album(id) ON DELETE CASCADE 27 + ); 28 + 29 + CREATE INDEX track_album_idx ON track(album_id); 30 + 31 + CREATE TABLE artist_track ( 32 + artist_id TEXT NOT NULL, 33 + track_id TEXT NOT NULL, 34 + 35 + PRIMARY KEY(artist_id, track_id), 36 + FOREIGN KEY(artist_id) REFERENCES artist(id), 37 + FOREIGN KEY(track_id) REFERENCES track(id) 38 + ); 39 + 40 + CREATE TABLE artist_album ( 41 + artist_id TEXT NOT NULL, 42 + album_id TEXT NOT NULL, 43 + 44 + PRIMARY KEY(artist_id, album_id), 45 + FOREIGN KEY(artist_id) REFERENCES artist(id), 46 + FOREIGN KEY(album_id) REFERENCES album(id) 47 + ); 48 + 49 + INSERT INTO migrations (name, applied_at) VALUES ("01-init", CURRENT_TIMESTAMP);
-61
src/library/library.odin
··· 29 29 Unable_To_Read_Dir, 30 30 } 31 31 32 - // read_dir :: proc( 33 - // path: string, 34 - // allocator: mem.Allocator = context.allocator, 35 - // ) -> ( 36 - // result: []formats.VorbisComment, 37 - // error: ReadError, 38 - // ) { 39 - // real_path, err := os.get_absolute_path(path, allocator) 40 - // defer delete(real_path) 41 - // 42 - // if err != nil { 43 - // return nil, .Unable_To_Resolve_Absolute_Path 44 - // } 45 - // 46 - // dir_entries: []os.File_Info 47 - // dir_entries, err = os.read_all_directory_by_path(real_path, allocator) 48 - // defer os.file_info_slice_delete(dir_entries, allocator) 49 - // 50 - // fmt.printfln("Dir entries: %v", dir_entries) 51 - // 52 - // if err != nil { 53 - // return nil, .Unable_To_Read_Dir 54 - // } 55 - // 56 - // dir_slice := dir_entries[:] 57 - // only_flac := slice.filter(dir_slice, is_flac) 58 - // defer delete(only_flac) 59 - // 60 - // fmt.printfln("only flac: %v", only_flac) 61 - // 62 - // length := len(only_flac) 63 - // 64 - // res := make([]formats.VorbisComment, length) 65 - // defer delete(res) 66 - // 67 - // file: ^os.File 68 - // ferr: os.Error 69 - // 70 - // for i in 0 ..< length { 71 - // entry := only_flac[i] 72 - // 73 - // fmt.printfln("entry %v", entry) 74 - // file, ferr = os.open(entry.fullpath, {.Read}) 75 - // if ferr != nil { 76 - // os.close(file) 77 - // continue 78 - // } 79 - // comment, err := formats.flac_read(file) 80 - // if err != nil { 81 - // os.close(file) 82 - // continue 83 - // } 84 - // res[i] = comment 85 - // os.close(file) 86 - // } 87 - // 88 - // fmt.printfln("\nFound: %v", len(res)) 89 - // 90 - // return res, nil 91 - // } 92 - 93 32 walk_dir :: proc(path: string, allocator := context.allocator) -> [dynamic]string { 94 33 95 34 w := os.walker_create(path)
+283
vendor/sqlite/addons/addons.odin
··· 1 + package addons 2 + 3 + import sqlite3 ".." 4 + import "base:runtime" 5 + import "core:c" 6 + import "core:fmt" 7 + import "core:log" 8 + import "core:mem" 9 + import "core:reflect" 10 + import "core:slice" 11 + import "core:strconv" 12 + import "core:strings" 13 + import "core:text/regex" 14 + 15 + Runtime_Config :: struct { 16 + extra_runtime_checks: bool, 17 + log_level: Maybe(log.Level), 18 + } 19 + 20 + config: Runtime_Config 21 + 22 + Query_Error :: Maybe(string) 23 + 24 + Query_Param_Value :: union { 25 + i32, 26 + i64, 27 + f64, 28 + []byte, 29 + bool, 30 + string, 31 + } 32 + 33 + Query_Param :: struct { 34 + index: int, 35 + value: Query_Param_Value, 36 + } 37 + 38 + query :: proc( 39 + db: ^sqlite3.Connection, 40 + out: ^[dynamic]$T, 41 + sql: string, 42 + params: []Query_Param = {}, 43 + location := #caller_location, 44 + ) -> sqlite3.Result_Code { 45 + stmt: ^sqlite3.Statement 46 + 47 + prepare(db, &stmt, sql, params, location) or_return 48 + return read_all_rows(stmt, out) 49 + } 50 + 51 + // Allocates. Make sure to free results even when the return value is not .Ok 52 + @(require_results) 53 + read_all_rows :: proc(stmt: ^sqlite3.Statement, out: ^[dynamic]$T) -> sqlite3.Result_Code { 54 + fields, err := get_type_fields(T) 55 + if err != nil { 56 + log.error(err) 57 + return .Internal 58 + } 59 + 60 + defer delete_field_types(fields) 61 + 62 + field_map: map[string]^Field_Type 63 + defer delete(field_map) 64 + for &field in fields { 65 + field_map[field.tag] = &field 66 + } 67 + 68 + defer sqlite3.finalize(stmt) 69 + for sqlite3.step(stmt) == .Row { 70 + item: T 71 + cols := sqlite3.column_count(stmt) 72 + for i in 0 ..< cols { 73 + column := strings.clone_from(sqlite3.column_name(stmt, i)) 74 + defer delete(column) 75 + 76 + field_type, ok := field_map[column] 77 + if !ok { 78 + log.errorf("could not find tag {} in {}", column, typeid_of(T)) 79 + return .Internal 80 + } 81 + 82 + if err := write_struct_field_from_statement(&item, field_type, stmt, c.int(i)); 83 + err != nil { 84 + log.error(err) 85 + free_query_error(err) 86 + return .Internal 87 + } 88 + } 89 + 90 + append(out, item) 91 + } 92 + 93 + return .Ok 94 + } 95 + 96 + @(require_results) 97 + execute :: proc( 98 + db: ^sqlite3.Connection, 99 + sql: string, 100 + params: []Query_Param = {}, 101 + location := #caller_location, 102 + ) -> sqlite3.Result_Code { 103 + stmt: ^sqlite3.Statement 104 + prepare(db, &stmt, sql, params, location) or_return 105 + defer sqlite3.finalize(stmt) 106 + for sqlite3.step(stmt) == .Row { 107 + // consume all rows 108 + } 109 + 110 + return .Ok 111 + } 112 + 113 + @(require_results) 114 + prepare :: proc( 115 + db: ^sqlite3.Connection, 116 + stmt: ^^sqlite3.Statement, 117 + sql: string, 118 + params: []Query_Param = {}, 119 + location := #caller_location, 120 + ) -> sqlite3.Result_Code { 121 + c_sql := strings.clone_to_cstring(sql) 122 + defer delete(c_sql) 123 + 124 + sqlite3.prepare_v2(db, c_sql, c.int(len(c_sql)), stmt, nil) or_return 125 + 126 + for &param in params { 127 + idx := c.int(param.index) 128 + 129 + if param.value == nil { 130 + sqlite3.bind_null(stmt^, idx) or_return 131 + } else if v, ok := param.value.(i32); ok { 132 + sqlite3.bind_int(stmt^, idx, c.int(v)) or_return 133 + } else if v, ok := param.value.(i64); ok { 134 + sqlite3.bind_int64(stmt^, idx, c.int64_t(v)) or_return 135 + } else if v, ok := param.value.([]byte); ok { 136 + sqlite3.bind_blob64( 137 + stmt^, 138 + idx, 139 + slice.as_ptr(v), 140 + c.int64_t(len(v)), 141 + {behaviour = .Static}, 142 + ) or_return 143 + } else if v, ok := param.value.(bool); ok { 144 + sqlite3.bind_int(stmt^, idx, c.int(v ? 1 : 0)) or_return 145 + } else if v, ok := param.value.(string); ok { 146 + // Sqlite treats our parameter as a "cstring" if we pass a negative length. 147 + // Explicitly it's just a slice. 148 + // https://sqlite.org/c3ref/bind_blob.html. 149 + cstr := strings.unsafe_string_to_cstring(v) 150 + sqlite3.bind_text(stmt^, idx, cstr, c.int(len(v)), {behaviour = .Static}) or_return 151 + } else { 152 + log.errorf("unhandled parameter type {}", param.value) 153 + return .Internal 154 + } 155 + 156 + } 157 + 158 + exp_statement := sqlite3.expanded_sql(stmt^) 159 + // `expanded_sql` can return NULL(nil) as explained here: https://www.sqlite.org/c3ref/expanded_sql.html 160 + if exp_statement != nil { 161 + defer sqlite3.free(cast(rawptr)exp_statement) 162 + do_log("SQL: {}", exp_statement, location = location) 163 + } else { 164 + // Not going to return an error here because everything else worked fine, 165 + // but it should be logged regardless. 166 + log.errorf("Unable to allocate memory while expanding the sql statement") 167 + } 168 + return .Ok 169 + } 170 + 171 + @(private) 172 + do_log :: #force_inline proc(format_str: string, args: ..any, location := #caller_location) { 173 + level, ok := config.log_level.? 174 + if !ok do return 175 + 176 + if context.logger.procedure != log.nil_logger_proc { 177 + log.logf(level, format_str, ..args, location = location) 178 + return 179 + } 180 + 181 + logger := log.create_console_logger() 182 + defer log.destroy_console_logger(logger) 183 + { 184 + context.logger = logger 185 + log.logf(level, format_str, ..args, location = location) 186 + } 187 + } 188 + 189 + @(private) 190 + free_query_error :: #force_inline proc(err: Query_Error) { 191 + delete(err.(string), context.temp_allocator) 192 + } 193 + 194 + @(private) 195 + @(require_results) 196 + write_struct_field_from_statement :: proc( 197 + obj: ^$T, 198 + field: ^Field_Type, 199 + stmt: ^sqlite3.Statement, 200 + col_idx: c.int, 201 + ) -> Query_Error { 202 + switch field.type.id { 203 + case typeid_of(string): 204 + value := strings.clone_from(sqlite3.column_text(stmt, col_idx)) 205 + write_struct_field(obj, field^, value) or_return 206 + 207 + case typeid_of(bool): 208 + value := sqlite3.column_int(stmt, col_idx) != 0 209 + write_struct_field(obj, field^, value) or_return 210 + 211 + case typeid_of(int): 212 + value := int(sqlite3.column_int(stmt, col_idx)) 213 + write_struct_field(obj, field^, value) or_return 214 + 215 + case typeid_of(uint): 216 + value := uint(sqlite3.column_int(stmt, col_idx)) 217 + write_struct_field(obj, field^, value) or_return 218 + 219 + case typeid_of(i8): 220 + value := i8(sqlite3.column_int(stmt, col_idx)) 221 + write_struct_field(obj, field^, value) or_return 222 + 223 + case typeid_of(u8): 224 + value := u8(sqlite3.column_int(stmt, col_idx)) 225 + write_struct_field(obj, field^, value) or_return 226 + 227 + case typeid_of(i16): 228 + value := i16(sqlite3.column_int(stmt, col_idx)) 229 + write_struct_field(obj, field^, value) or_return 230 + 231 + case typeid_of(u16): 232 + value := u16(sqlite3.column_int(stmt, col_idx)) 233 + write_struct_field(obj, field^, value) or_return 234 + 235 + case typeid_of(i32): 236 + value := i32(sqlite3.column_int(stmt, col_idx)) 237 + write_struct_field(obj, field^, value) or_return 238 + 239 + case typeid_of(u32): 240 + value := u32(sqlite3.column_int(stmt, col_idx)) 241 + write_struct_field(obj, field^, value) or_return 242 + 243 + case typeid_of(i64): 244 + value := i64(sqlite3.column_int64(stmt, col_idx)) 245 + write_struct_field(obj, field^, value) or_return 246 + 247 + case typeid_of(u64): 248 + value := u64(sqlite3.column_int64(stmt, col_idx)) 249 + write_struct_field(obj, field^, value) or_return 250 + 251 + case: 252 + if reflect.is_enum(field.type) { 253 + enum_values := reflect.enum_field_values(field.type.id) 254 + 255 + value := i64(sqlite3.column_int64(stmt, col_idx)) 256 + 257 + if config.extra_runtime_checks { 258 + found := false 259 + 260 + for it in enum_values { 261 + if i64(it) == value { 262 + found = true 263 + break 264 + } 265 + } 266 + 267 + if !found { 268 + return fmt.tprintf( 269 + "expected to find enum value {} in {}", 270 + value, 271 + field.type.id, 272 + ) 273 + } 274 + } 275 + 276 + write_struct_field(obj, field^, value) or_return 277 + } else { 278 + return fmt.tprintf("unhandled data type {}", field.type.id) 279 + } 280 + } 281 + 282 + return nil 283 + }
+196
vendor/sqlite/addons/fields.odin
··· 1 + package addons 2 + 3 + import "core:fmt" 4 + import "core:reflect" 5 + import "core:strings" 6 + import "core:testing" 7 + 8 + Field_Error :: Maybe(string) 9 + 10 + @(private) 11 + Field_Type :: struct { 12 + name: string, 13 + type: ^reflect.Type_Info, 14 + tag: string, 15 + offset: int, 16 + } 17 + 18 + @(private) 19 + write_struct_field :: proc(obj: ^$T, field: Field_Type, value: $E) -> Field_Error { 20 + if !(size_of(field.type.id) == size_of(E) || field.type.id == typeid_of(E)) { 21 + return fmt.tprintf( 22 + "given field {}.{} ({}) is not the same size as given value {}. {} != {}", 23 + typeid_of(T), 24 + field.name, 25 + field.type.id, 26 + typeid_of(E), 27 + size_of(field.type.id), 28 + size_of(E), 29 + ) 30 + } 31 + 32 + obj_bytes := reflect.as_bytes(obj^) 33 + value_bytes := reflect.as_bytes(value) 34 + for i in 0 ..< len(value_bytes) { 35 + obj_bytes[int(field.offset) + i] = value_bytes[i] 36 + } 37 + 38 + return nil 39 + } 40 + 41 + @(test) 42 + write_struct_field_test :: proc(t: ^testing.T) { 43 + S :: struct($T: typeid) { 44 + padding1: [13]u8 `sqlite:"Padding1"`, 45 + value: T `sqlite:"Value"`, 46 + padding2: [5]u8 `sqlite:"Padding2"`, 47 + } 48 + 49 + test_using_type :: proc(t: ^testing.T, value: $T) { 50 + S1 :: S(T) 51 + fields, err := get_type_fields(S1) 52 + testing.expect(t, err == nil) 53 + defer delete_field_types(fields) 54 + 55 + value_field := fields[1] 56 + obj: S1 57 + 58 + err = write_struct_field(&obj, value_field, value) 59 + testing.expect(t, err == nil) 60 + testing.expect_value(t, obj.value, value) 61 + 62 + // neighbours are unchanged 63 + for b in obj.padding1 { 64 + testing.expect(t, b == 0) 65 + } 66 + 67 + // neighbours are unchanged 68 + for b in obj.padding2 { 69 + testing.expect(t, b == 0) 70 + } 71 + } 72 + 73 + test_using_type(t, u8(100)) 74 + test_using_type(t, i8(100)) 75 + test_using_type(t, true) 76 + test_using_type(t, i16(100)) 77 + test_using_type(t, u16(100)) 78 + test_using_type(t, i32(100)) 79 + test_using_type(t, u32(100)) 80 + test_using_type(t, i64(100)) 81 + test_using_type(t, u64(100)) 82 + test_using_type(t, "hello") 83 + } 84 + 85 + @(private) 86 + field_type_deinit :: proc(field: ^Field_Type) { 87 + delete(field.name) 88 + delete(field.tag) 89 + } 90 + 91 + @(private) 92 + delete_field_types :: proc(field_types: []Field_Type) { 93 + for &it in field_types { 94 + field_type_deinit(&it) 95 + } 96 + 97 + delete(field_types) 98 + } 99 + 100 + @(private) 101 + @(require_results) 102 + get_type_fields :: proc($T: typeid) -> ([]Field_Type, Field_Error) { 103 + out: [dynamic]Field_Type 104 + struct_info := type_info_of(T) 105 + for i in 0 ..< reflect.struct_field_count(T) { 106 + field := reflect.struct_field_at(T, i) 107 + capture, err := match_and_return_capture(`sqlite:"(.*?)"`, cast(string)field.tag) 108 + if err != nil { 109 + defer delete_field_types(out[:]) 110 + 111 + return nil, fmt.tprintf( 112 + "could not find sqlite tag on field '{}' of struct '{}'. err: {}", 113 + field.name, 114 + struct_info, 115 + err, 116 + ) 117 + } 118 + 119 + append( 120 + &out, 121 + Field_Type { 122 + tag = capture, 123 + name = strings.clone(field.name), 124 + type = field.type, 125 + offset = int(field.offset), 126 + }, 127 + ) 128 + 129 + } 130 + 131 + return out[:], nil 132 + } 133 + 134 + 135 + @(test) 136 + get_type_fields_test__all_ok :: proc(t: ^testing.T) { 137 + S :: struct { 138 + a: int `sqlite:"Foo"`, 139 + b: bool `sqlite:"Bar"`, 140 + c: string `sqlite:"Foobar"`, 141 + } 142 + 143 + fields, err := get_type_fields(S) 144 + testing.expect(t, err == nil) 145 + defer delete_field_types(fields) 146 + 147 + testing.expect(t, fields[0].tag == "Foo") 148 + testing.expect(t, fields[0].name == "a") 149 + testing.expect(t, fields[0].type.id == typeid_of(int)) 150 + 151 + testing.expect(t, fields[1].tag == "Bar") 152 + testing.expect(t, fields[1].name == "b") 153 + testing.expect(t, fields[1].type.id == typeid_of(bool)) 154 + 155 + testing.expect(t, fields[2].tag == "Foobar") 156 + testing.expect(t, fields[2].name == "c") 157 + testing.expect(t, fields[2].type.id == typeid_of(string)) 158 + } 159 + 160 + 161 + @(test) 162 + get_type_fields_test__missing_tag :: proc(t: ^testing.T) { 163 + S :: struct { 164 + a: int `sqlite:"Foo"`, 165 + b: bool, 166 + c: string `sqlite:"Foobar"`, 167 + } 168 + 169 + fields, err := get_type_fields(S) 170 + testing.expect(t, err != nil) 171 + } 172 + 173 + 174 + @(test) 175 + get_type_fields_test__malformed_tag :: proc(t: ^testing.T) { 176 + S :: struct { 177 + a: int `sqlite:"Foo"`, 178 + b: bool `sqlit:"Bar"`, 179 + c: string `sqlite:"Foobar"`, 180 + } 181 + 182 + fields, err := get_type_fields(S) 183 + testing.expect(t, err != nil) 184 + } 185 + 186 + @(test) 187 + get_type_fields_test__malformed_tag_missing_quote :: proc(t: ^testing.T) { 188 + S :: struct { 189 + a: int `sqlite:"Foo"`, 190 + b: bool `sqlite:"Bar`, 191 + c: string `sqlite:"Foobar"`, 192 + } 193 + 194 + fields, err := get_type_fields(S) 195 + testing.expect(t, err != nil) 196 + }
+53
vendor/sqlite/addons/regex.odin
··· 1 + package addons 2 + 3 + import "core:fmt" 4 + import "core:strings" 5 + import "core:testing" 6 + import "core:text/regex" 7 + 8 + Capture_Error :: enum { 9 + None, 10 + No_Capture, 11 + } 12 + 13 + Regex_Error :: union #shared_nil { 14 + Capture_Error, 15 + regex.Error, 16 + } 17 + 18 + @(private) 19 + match_and_return_capture :: proc(pattern: string, str: string) -> (string, Regex_Error) { 20 + defer free_all(context.temp_allocator) 21 + regexp, err := regex.create(pattern, permanent_allocator = context.temp_allocator) 22 + if err != nil { 23 + return "", err 24 + } 25 + 26 + capture, ok := regex.match_and_allocate_capture(regexp, str, context.temp_allocator) 27 + if !ok { 28 + return "", .No_Capture 29 + } 30 + 31 + 32 + return strings.clone(capture.groups[1]), nil 33 + } 34 + 35 + @(test) 36 + match_and_return_capture_test__no_capture :: proc(t: ^testing.T) { 37 + capture, err := match_and_return_capture(`\<(.*)\>`, "hello world") 38 + testing.expect_value(t, err, Capture_Error.No_Capture) 39 + } 40 + 41 + @(test) 42 + match_and_return_capture_test__malformed_regex :: proc(t: ^testing.T) { 43 + capture, err := match_and_return_capture(`?\<(.*)\>`, "<hello world>") 44 + testing.expect(t, err != nil) 45 + } 46 + 47 + @(test) 48 + match_and_return_capture_test :: proc(t: ^testing.T) { 49 + capture, err := match_and_return_capture(`\<(.*)\>`, "<hello world>") 50 + testing.expect(t, err == nil) 51 + testing.expect(t, capture == "hello world") 52 + delete(capture) 53 + }
+353
vendor/sqlite/sqlite.odin
··· 1 + package sqlite3 2 + 3 + import "core:c" 4 + 5 + Connection :: rawptr 6 + Backup :: rawptr 7 + Statement :: rawptr 8 + Blob :: rawptr 9 + 10 + @(private) 11 + USE_DYNAMIC_LIB :: #config(SQLITE3_DYNAMIC_LIB, false) 12 + @(private) 13 + USE_SYSTEM_LIB :: #config(SQLITE3_SYSTEM_LIB, false) 14 + @(private) 15 + USE_SQLCIPHER :: #config(SQLITE3_USE_SQLCIPHER, false) 16 + 17 + when ODIN_OS == .Windows { 18 + when USE_SYSTEM_LIB { 19 + when USE_DYNAMIC_LIB { 20 + when USE_SQLCIPHER { 21 + foreign import sqlite "system:libsqlcipher.dll" 22 + } else { 23 + foreign import sqlite "system:libsqlite3.dll" 24 + } 25 + } else { 26 + when USE_SQLCIPHER { 27 + foreign import sqlite "system:libsqlcipher.lib" 28 + } else { 29 + foreign import sqlite "system:libsqlite3.lib" 30 + } 31 + } 32 + } else { 33 + when USE_DYNAMIC_LIB { 34 + when USE_SQLCIPHER { 35 + foreign import sqlite "libsqlcipher.dll" 36 + } else { 37 + foreign import sqlite "libsqlite3.dll" 38 + } 39 + } else { 40 + when USE_SQLCIPHER { 41 + foreign import sqlite "libsqlcipher.lib" 42 + } else { 43 + foreign import sqlite "libsqlite3.lib" 44 + } 45 + } 46 + } 47 + } else when ODIN_OS == .Darwin { 48 + when USE_SYSTEM_LIB { 49 + when USE_DYNAMIC_LIB { 50 + when USE_SQLCIPHER { 51 + foreign import sqlite "system:libsqlcipher.dylib" 52 + } else { 53 + foreign import sqlite "system:libsqlite3.dylib" 54 + } 55 + } else { 56 + when USE_SQLCIPHER { 57 + foreign import sqlite "system:libsqlcipher.a" 58 + } else { 59 + foreign import sqlite "system:libsqlite3.a" 60 + } 61 + } 62 + } else { 63 + when USE_DYNAMIC_LIB { 64 + when USE_SQLCIPHER { 65 + foreign import sqlite "libsqlcipher.dylib" 66 + } else { 67 + foreign import sqlite "libsqlite3.dylib" 68 + } 69 + } else { 70 + when USE_SQLCIPHER { 71 + foreign import sqlite "libsqlcipher.a" 72 + } else { 73 + foreign import sqlite "libsqlite3.a" 74 + } 75 + } 76 + } 77 + } else when ODIN_OS == .Linux { 78 + when USE_SYSTEM_LIB { 79 + when USE_DYNAMIC_LIB { 80 + when USE_SQLCIPHER { 81 + foreign import sqlite "system:libsqlcipher.so" 82 + } else { 83 + foreign import sqlite "system:libsqlite3.so" 84 + } 85 + } else { 86 + when USE_SQLCIPHER { 87 + foreign import sqlite "system:libsqlcipher.a" 88 + } else { 89 + foreign import sqlite "system:libsqlite3.a" 90 + } 91 + } 92 + } else { 93 + when USE_DYNAMIC_LIB { 94 + when USE_SQLCIPHER { 95 + foreign import sqlite "libsqlcipher.so" 96 + } else { 97 + foreign import sqlite "libsqlite3.so" 98 + } 99 + } else { 100 + when USE_SQLCIPHER { 101 + foreign import sqlite "libsqlcipher.a" 102 + } else { 103 + foreign import sqlite "libsqlite3.a" 104 + } 105 + } 106 + } 107 + } 108 + 109 + Config_Option :: enum (c.int) { 110 + Single_Thread = 1, 111 + Multi_Thread = 2, 112 + Serialized = 3, 113 + Malloc = 4, 114 + Get_Malloc = 5, 115 + Scratch = 6, 116 + Page_Cache = 7, 117 + Heap = 8, 118 + Mem_Status = 9, 119 + Mutex = 10, 120 + Get_Mutex = 11, 121 + Lookaside = 13, 122 + PCache = 14, 123 + Get_PCache = 15, 124 + Log = 16, 125 + Uri = 17, 126 + PCache2 = 18, 127 + Get_PCache2 = 19, 128 + Covering_Index_Scan = 20, 129 + SqlLog = 21, 130 + Mmap_Size = 22, 131 + Win32_Heapsize = 23, 132 + PCache_Hdrsz = 24, 133 + Pmasz = 25, 134 + StmtJrnl_Spill = 26, 135 + Small_Malloc = 27, 136 + SorterRef_Size = 28, 137 + Memdb_Maxsize = 29, 138 + Rowid_In_View = 30, 139 + } 140 + 141 + Destructor_Behavior :: enum (int) { 142 + Static = 0, 143 + Transient = -1, 144 + } 145 + 146 + Destructor :: struct #raw_union { 147 + callback: proc(it: rawptr), 148 + behaviour: Destructor_Behavior, 149 + } 150 + 151 + Result_Code :: enum (c.int) { 152 + Ok = 0, 153 + Error = 1, 154 + Internal = 2, 155 + Perm = 3, 156 + Abort = 4, 157 + Busy = 5, 158 + Locked = 6, 159 + NoMem = 7, 160 + ReadOnly = 8, 161 + Interrupt = 9, 162 + IoErr = 10, 163 + Corrupt = 11, 164 + NotFound = 12, 165 + Full = 13, 166 + CantOpen = 14, 167 + Protocol = 15, 168 + Empty = 16, 169 + Schema = 17, 170 + TooBig = 18, 171 + Constraint = 19, 172 + Mismatch = 20, 173 + Misuse = 21, 174 + NoLfs = 22, 175 + Auth = 23, 176 + Format = 24, 177 + Range = 25, 178 + NotA_Db = 26, 179 + Notice = 27, 180 + Warning = 28, 181 + Row = 100, 182 + Done = 101, 183 + Ok_Load_Permanently = 256, 184 + Error_Missing_CollSeq = 257, 185 + Busy_Recovery = 261, 186 + Locked_SharedCache = 262, 187 + ReadOnly_Recovery = 264, 188 + IoErr_Read = 266, 189 + Corrupt_Vtab = 267, 190 + CantOpen_NoTempDir = 270, 191 + Constraint_Check = 275, 192 + Auth_User = 279, 193 + Notice_Recover_Wal = 283, 194 + Warning_AutoIndex = 284, 195 + Error_Retry = 513, 196 + Abort_Rollback = 516, 197 + Busy_Snapshot = 517, 198 + Locked_Vtab = 518, 199 + ReadOnly_CantLock = 520, 200 + IoErr_Short_Read = 522, 201 + Corrupt_Sequence = 523, 202 + CantOpen_IsDir = 526, 203 + Constraint_CommitHook = 531, 204 + Notice_Recover_Rollback = 539, 205 + Error_Snapshot = 769, 206 + Busy_Timeout = 773, 207 + ReadOnly_Rollback = 776, 208 + IoErr_Write = 778, 209 + Corrupt_Index = 779, 210 + CantOpen_FullPath = 782, 211 + Constraint_ForeignKey = 787, 212 + ReadOnly_DbMoved = 1032, 213 + IoErr_FSync = 1034, 214 + CantOpen_ConvPath = 1038, 215 + Constraint_Function = 1043, 216 + ReadOnly_CantInit = 1288, 217 + IoErr_Dir_FSync = 1290, 218 + CantOpen_DirtyWal = 1294, 219 + Constraint_NotNull = 1299, 220 + ReadOnly_Directory = 1544, 221 + IoErr_Truncate = 1546, 222 + CantOpen_Symlink = 1550, 223 + Constraint_PrimaryKey = 1555, 224 + IoErr_FStat = 1802, 225 + Constraint_Trigger = 1811, 226 + IoErr_Unlock = 2058, 227 + Constraint_Unique = 2067, 228 + IoErr_RdLock = 2314, 229 + Constraint_Vtab = 2323, 230 + IoErr_Delete = 2570, 231 + Constraint_RowId = 2579, 232 + IoErr_Blocked = 2826, 233 + Constraint_Pinned = 2835, 234 + IoErr_NoMem = 3082, 235 + Constraint_DataType = 3091, 236 + IoErr_Access = 3338, 237 + IoErr_CheckReservedLock = 3594, 238 + IoErr_Lock = 3850, 239 + IoErr_Close = 4106, 240 + IoErr_Dir_Close = 4362, 241 + IoErr_ShmOpen = 4618, 242 + IoErr_ShmSize = 4874, 243 + IoErr_ShmLock = 5130, 244 + IoErr_ShmMap = 5386, 245 + IoErr_Seek = 5642, 246 + IoErr_Delete_NoEnt = 5898, 247 + IoErr_MMap = 6154, 248 + IoErr_GetTempPath = 6410, 249 + IoErr_ConvPath = 6666, 250 + IoErr_VNode = 6922, 251 + IoErr_Auth = 7178, 252 + IoErr_Begin_Atomic = 7434, 253 + IoErr_Commit_Atomic = 7690, 254 + IoErr_Rollback_Atomic = 7946, 255 + IoErr_Data = 8202, 256 + IoErr_CorruptFS = 8458, 257 + } 258 + 259 + @(link_prefix = "sqlite3_") 260 + foreign sqlite { 261 + free :: proc "c" (ptr: rawptr) --- 262 + open :: proc "c" (filename: cstring, db: ^^Connection) -> Result_Code --- 263 + open16 :: proc "c" (filename: cstring, db: ^^Connection) -> Result_Code --- 264 + open_v2 :: proc "c" (filename: cstring, db: ^^Connection, flags: c.int, z_vfs: cstring) -> Result_Code --- 265 + close :: proc "c" (db: ^Connection) -> Result_Code --- 266 + close_v2 :: proc "c" (db: ^Connection) -> Result_Code --- 267 + prepare :: proc "c" (db: ^Connection, sql: cstring, n_bytes: c.int, statement: ^^Statement, tail: ^^cstring) -> Result_Code --- 268 + prepare_v2 :: proc "c" (db: ^Connection, sql: cstring, n_bytes: c.int, statement: ^^Statement, tail: ^^cstring) -> Result_Code --- 269 + step :: proc "c" (statement: ^Statement) -> Result_Code --- 270 + finalize :: proc "c" (statememt: ^Statement) -> Result_Code --- 271 + exec :: proc "c" (db: ^Connection, sql: cstring, cb: proc "c" (ctx: rawptr, argc: c.int, argv: [^]cstring, col_names: [^]cstring) -> c.int, ctx: rawptr, err: ^cstring) -> Result_Code --- 272 + changes :: proc "c" (db: ^Connection) -> c.int --- 273 + changes64 :: proc "c" (db: ^Connection) -> c.int64_t --- 274 + errmsg :: proc "c" (db: ^Connection) -> cstring --- 275 + extended_result_codes :: proc "c" (db: ^Connection, onoff: c.int) -> c.int --- 276 + extended_errcode :: proc "c" (db: ^Connection) -> Result_Code --- 277 + auto_extension :: proc "c" (x_entry_point: proc "c" ()) -> Result_Code --- 278 + cancel_auto_extension :: proc "c" (x_entry_point: proc "c" ()) -> Result_Code --- 279 + backup :: proc "c" (dest: ^Connection, dest_name: cstring, source: ^Connection, source_name: cstring) -> ^Backup --- 280 + backup_step :: proc "c" (backup: ^Backup, n_page: c.int) -> c.int --- 281 + backup_finish :: proc "c" (backup: ^Backup) -> Result_Code --- 282 + backup_remaining :: proc "c" (backup: ^Backup) -> c.int --- 283 + backup_pagecount :: proc "c" (backup: ^Backup) -> c.int --- 284 + bind_blob :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: [^]byte, param_len: c.int, free: Destructor) -> Result_Code --- 285 + bind_blob64 :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: [^]byte, param_len: c.int64_t, free: Destructor) -> Result_Code --- 286 + bind_double :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: c.double) -> Result_Code --- 287 + bind_int :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: c.int) -> Result_Code --- 288 + bind_int64 :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: c.int64_t) -> Result_Code --- 289 + bind_null :: proc "c" (statement: ^Statement, param_idx: c.int) -> Result_Code --- 290 + bind_text :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: cstring, param_len: c.int, free: Destructor) -> Result_Code --- 291 + bind_text16 :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: cstring, param_len: c.int, free: Destructor) -> Result_Code --- 292 + bind_text64 :: proc "c" (statement: ^Statement, param_idx: c.int, param_value: cstring, param_len: c.int64_t, free: Destructor, encoding: c.uchar) -> Result_Code --- 293 + bind_zeroblob :: proc "c" (statement: ^Statement, param_idx: c.int, len: c.int) -> Result_Code --- 294 + bind_zeroblob64 :: proc "c" (statement: ^Statement, param_idx: c.int, len: c.int64_t) -> Result_Code --- 295 + bind_parameter_count :: proc "c" (statement: ^Statement) -> c.int --- 296 + bind_parameter_index :: proc "c" (statement: ^Statement, name: cstring) -> c.int --- 297 + bind_parameter_name :: proc "c" (statement: ^Statement, param_idx: c.int) -> cstring --- 298 + blob_bytes :: proc "c" (blob: ^Blob) -> c.int --- 299 + blob_close :: proc "c" (blob: ^Blob) -> Result_Code --- 300 + blob_open :: proc "c" (db: ^Connection, database_name: cstring, table: cstring, column: cstring, row_idx: c.int64_t, flags: c.int, blob: ^^Blob) -> Result_Code --- 301 + blob_read :: proc "c" (blob: ^Blob, dest: rawptr, n_bytes: c.int, n_bytes_offset: c.int) -> Result_Code --- 302 + blob_write :: proc "c" (blob: ^Blob, source: rawptr, n_bytes: c.int, n_bytes_offset: c.int) -> Result_Code --- 303 + blob_reopen :: proc "c" (blob: ^Blob, row_id: c.int64_t) -> Result_Code --- 304 + busy_handler :: proc "c" (db: ^Connection, handler: proc "c" (ctx: rawptr, attempt: c.int) -> Result_Code, ctx: rawptr) -> Result_Code --- 305 + busy_timeout :: proc "c" (db: ^Connection, ms: c.int) -> Result_Code --- 306 + @(require_results) 307 + column_database_name :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 308 + @(require_results) 309 + column_database_name16 :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 310 + @(require_results) 311 + column_table_name :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 312 + @(require_results) 313 + column_table_name16 :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 314 + @(require_results) 315 + column_origin_name :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 316 + @(require_results) 317 + column_origin_name16 :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 318 + @(require_results) 319 + column_decltype :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 320 + @(require_results) 321 + column_decltype16 :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 322 + @(require_results) 323 + column_text :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 324 + column_blob :: proc "c" (statement: ^Statement, col_idx: c.int) -> rawptr --- 325 + column_double :: proc "c" (statement: ^Statement, col_idx: c.int) -> c.double --- 326 + column_int :: proc "c" (statement: ^Statement, col_idx: c.int) -> c.int --- 327 + column_int64 :: proc "c" (statement: ^Statement, col_idx: c.int) -> c.int64_t --- 328 + column_bytes :: proc "c" (statement: ^Statement, col_idx: c.int) -> c.int --- 329 + column_bytes16 :: proc "c" (statement: ^Statement, col_idx: c.int) -> c.int --- 330 + column_type :: proc "c" (statement: ^Statement, col_idx: c.int) -> c.int --- 331 + column_count :: proc "c" (statement: ^Statement) -> c.int --- 332 + column_name :: proc "c" (statement: ^Statement, col_idx: c.int) -> cstring --- 333 + commit_hook :: proc "c" (db: ^Connection, cb: proc "c" (ctx: rawptr) -> Result_Code, ctx: rawptr) -> rawptr --- 334 + rollback_hook :: proc "c" (db: ^Connection, cb: proc "c" (ctx: rawptr) -> Result_Code, ctx: rawptr) -> rawptr --- 335 + compileoption_used :: proc "c" (opt_name: cstring) -> c.int --- 336 + @(require_results) 337 + compileoption_get :: proc "c" (n: c.int) -> cstring --- 338 + complete :: proc "c" (sql: cstring) -> c.int --- 339 + complete16 :: proc "c" (sql: cstring) -> c.int --- 340 + // TODO: this function should also have varargs 341 + config :: proc "c" (option: Config_Option) -> Result_Code --- 342 + sql :: proc "c" (statement: ^Statement) -> cstring --- 343 + expanded_sql :: proc "c" (statement: ^Statement) -> cstring --- 344 + threadsafe :: proc "c" () -> c.int --- 345 + 346 + // Export SQLCipher-specific functions conditionally. 347 + when USE_SQLCIPHER { 348 + key :: proc "c" (db: ^Connection, key: rawptr, nKey: c.int) -> c.int --- 349 + key_v2 :: proc "c" (db: ^Connection, zDbName: cstring, key: rawptr, nKey: c.int) -> c.int --- 350 + rekey :: proc "c" (db: ^Connection, key: rawptr, nKey: c.int) -> c.int --- 351 + rekey_v2 :: proc "c" (db: ^Connection, zDbName: cstring, key: rawptr, nKey: c.int) -> c.int --- 352 + } 353 + }