Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
09b502404b |
10 changed files with 146 additions and 33 deletions
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
||||||
use flake
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1 @@
|
||||||
/target
|
/target
|
||||||
|
|
||||||
.direnv/
|
|
||||||
|
|
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -284,8 +284,15 @@ dependencies = [
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"time",
|
"time",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1_smol"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
|
@ -311,6 +318,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"time",
|
"time",
|
||||||
|
"uuid",
|
||||||
"xdg",
|
"xdg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -364,6 +372,15 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||||
|
dependencies = [
|
||||||
|
"sha1_smol",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
|
@ -13,8 +13,9 @@ maintenance = { status = "experimental" }
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3.2.16", default-features = false, features = ["derive", "std"] }
|
clap = { version = "3.2.16", default-features = false, features = ["derive", "std"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled", "time"] }
|
rusqlite = { version = "0.28.0", features = ["bundled", "time", "uuid"] }
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
|
uuid = { version = "1.1.2", features = ["v5"] }
|
||||||
xdg = "2.4.1"
|
xdg = "2.4.1"
|
||||||
|
|
||||||
|
|
||||||
|
|
2
database/migrations/202208211624.sql
Normal file
2
database/migrations/202208211624.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE tasks
|
||||||
|
ADD COLUMN uuid BLOB;
|
|
@ -8,6 +8,7 @@ CREATE TABLE tasks (
|
||||||
project TEXT ,
|
project TEXT ,
|
||||||
link TEXT ,
|
link TEXT ,
|
||||||
dir_path TEXT ,
|
dir_path TEXT ,
|
||||||
|
uuid BLOB ,
|
||||||
|
|
||||||
current BOOLEAN NOT NULL DEFAULT false,
|
current BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
|
|
36
flake.lock
36
flake.lock
|
@ -1,6 +1,39 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"naersk": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1659610603,
|
||||||
|
"narHash": "sha256-LYgASYSPYo7O71WfeUOaEUzYfzuXm8c8eavJcel+pfI=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"rev": "c6a45e4277fa58abd524681466d3450f896dc094",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "naersk",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1661008273,
|
||||||
|
"narHash": "sha256-UpDqsGzUswIHG7FwzeIewjWlElF17UVLNbI2pwlbcBY=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "0cc6444e74cd21e8da8d81ef4cd778492e10f843",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1661008273,
|
"lastModified": 1661008273,
|
||||||
"narHash": "sha256-UpDqsGzUswIHG7FwzeIewjWlElF17UVLNbI2pwlbcBY=",
|
"narHash": "sha256-UpDqsGzUswIHG7FwzeIewjWlElF17UVLNbI2pwlbcBY=",
|
||||||
|
@ -18,7 +51,8 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs",
|
"naersk": "naersk",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
39
flake.nix
39
flake.nix
|
@ -1,30 +1,45 @@
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
|
naersk.url = "github:nix-community/naersk/master";
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
utils.url = "github:numtide/flake-utils";
|
utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, utils }:
|
outputs = { self, nixpkgs, utils, naersk }:
|
||||||
utils.lib.eachDefaultSystem (system:
|
utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
|
name = "tas";
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
cargoToml = with builtins; (fromTOML (readFile ./Cargo.toml));
|
naersk-lib = pkgs.callPackage naersk { };
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
packages.default = pkgs.rustPlatform.buildRustPackage {
|
# Executes by `nix build .#<name>`
|
||||||
inherit (cargoToml.package) name version;
|
packages = {
|
||||||
src = nixpkgs.lib.cleanSource ./.;
|
${name} = naersk.lib.${system}.buildPackage {
|
||||||
doCheck = true;
|
pname = name;
|
||||||
cargoLock.lockFile = ./Cargo.lock;
|
root = ./.;
|
||||||
};
|
};
|
||||||
|
|
||||||
apps.default = utils.lib.mkApp {
|
|
||||||
inherit (cargoToml.package) name;
|
|
||||||
drv = packages.default;
|
|
||||||
};
|
};
|
||||||
|
# Executes by `nix build .`
|
||||||
|
packages.default = packages.${name};
|
||||||
|
# the same but deprecated in Nix 2.7
|
||||||
|
defaultPackage = packages.default;
|
||||||
|
|
||||||
|
# Executes by `nix run .#<name> -- <args?>`
|
||||||
|
apps = {
|
||||||
|
${name} = utils.lib.mkApp {
|
||||||
|
inherit name;
|
||||||
|
drv = packages.${name};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# Executes by `nix run . -- <args?>`
|
||||||
|
apps.default = apps.${name};
|
||||||
|
# the same but deprecated in Nix 2.7
|
||||||
|
defaultApp = apps.default;
|
||||||
|
|
||||||
|
# Used by `nix develop`
|
||||||
devShell = with pkgs; mkShell {
|
devShell = with pkgs; mkShell {
|
||||||
packages = [ cargo rustc rustfmt clippy rust-analyzer ];
|
buildInputs = [ cargo rustc rustfmt rustPackages.clippy ];
|
||||||
RUST_SRC_PATH = rustPlatform.rustLibSrc;
|
RUST_SRC_PATH = rustPlatform.rustLibSrc;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub struct UpdateTaskData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Repository {
|
pub trait Repository {
|
||||||
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error>;
|
fn get_current_task_opt(&self) -> Result<Option<domain::Task>, Error>;
|
||||||
|
|
||||||
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error>;
|
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error>;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use rusqlite::{Connection, OptionalExtension};
|
use rusqlite::{Connection, OptionalExtension};
|
||||||
|
use uuid::Uuid;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
use crate::domain;
|
use crate::domain;
|
||||||
|
@ -18,7 +19,7 @@ struct Task {
|
||||||
*/
|
*/
|
||||||
created_at: time::OffsetDateTime,
|
created_at: time::OffsetDateTime,
|
||||||
|
|
||||||
idx: i64,
|
uuid: uuid::Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Task> for domain::Task {
|
impl From<Task> for domain::Task {
|
||||||
|
@ -48,7 +49,7 @@ impl<'r> TryFrom<&'r rusqlite::Row<'_>> for Task {
|
||||||
finished_at: row.get("finished_at")?,
|
finished_at: row.get("finished_at")?,
|
||||||
*/
|
*/
|
||||||
created_at: row.get("created_at")?,
|
created_at: row.get("created_at")?,
|
||||||
idx: row.get("idx")?,
|
uuid: row.get("uuid")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,13 +70,10 @@ impl SqliteRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repository for SqliteRepo {
|
impl Repository for SqliteRepo {
|
||||||
fn get_current_task_opt(&self) -> Result<Option<domain::CurrentTaskInfo>, Error> {
|
fn get_current_task_opt(&self) -> Result<Option<domain::Task>, Error> {
|
||||||
let db_task = self.get_current_task_opt_impl()?;
|
let db_task = self.get_current_task_opt_impl()?;
|
||||||
|
|
||||||
Ok(db_task.map(|db_task| domain::CurrentTaskInfo {
|
Ok(db_task.map(|db_task| db_task.into()))
|
||||||
task_idx: db_task.idx as usize,
|
|
||||||
task: db_task.into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error> {
|
fn get_task_opt(&self, id: domain::TaskIdx) -> Result<Option<domain::Task>, Error> {
|
||||||
|
@ -118,11 +116,13 @@ impl Repository for SqliteRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_task(&self, insert_data: super::InsertTaskData) -> Result<domain::Task, Error> {
|
fn insert_task(&self, insert_data: super::InsertTaskData) -> Result<domain::Task, Error> {
|
||||||
|
let created_at = time::OffsetDateTime::now_utc();
|
||||||
|
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare(
|
.prepare(
|
||||||
"INSERT INTO tasks (name, project, link, dir_path)
|
"INSERT INTO tasks (name, project, link, dir_path, created_at)
|
||||||
VALUES (?1, ?2, ?3, ?4)",
|
VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::PrepareQuery)?;
|
.map_err(|_| Error::PrepareQuery)?;
|
||||||
|
|
||||||
|
@ -134,6 +134,7 @@ impl Repository for SqliteRepo {
|
||||||
&insert_data
|
&insert_data
|
||||||
.dir_path
|
.dir_path
|
||||||
.and_then(|p| p.into_os_string().into_string().ok()),
|
.and_then(|p| p.into_os_string().into_string().ok()),
|
||||||
|
&created_at,
|
||||||
))
|
))
|
||||||
.map_err(|_| Error::InsertData)?;
|
.map_err(|_| Error::InsertData)?;
|
||||||
|
|
||||||
|
@ -243,7 +244,7 @@ impl SqliteRepo {
|
||||||
fn get_current_task_opt_impl(&self) -> Result<Option<Task>, Error> {
|
fn get_current_task_opt_impl(&self) -> Result<Option<Task>, Error> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare("SELECT * FROM active_tasks WHERE current IS true")
|
.prepare("SELECT * FROM active_tasks WHERE current")
|
||||||
.map_err(|_| Error::PrepareQuery)?;
|
.map_err(|_| Error::PrepareQuery)?;
|
||||||
|
|
||||||
let row = stmt
|
let row = stmt
|
||||||
|
@ -257,10 +258,14 @@ impl SqliteRepo {
|
||||||
|
|
||||||
macro_rules! run_migration {
|
macro_rules! run_migration {
|
||||||
($this:ident, $ver:ident = $version:expr) => {
|
($this:ident, $ver:ident = $version:expr) => {
|
||||||
run_migration!($this, $ver = $version => concat!("migrations/", $version));
|
run_migration!(
|
||||||
|
$this,
|
||||||
|
$ver = $version,
|
||||||
|
file = concat!("migrations/", $version)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
($this:ident, $ver:ident = $version:expr => $sql_name:expr) => {
|
($this:ident, $ver:ident = $version:expr, file = $sql_name:expr) => {
|
||||||
$this
|
$this
|
||||||
.conn
|
.conn
|
||||||
.execute_batch(&format!(
|
.execute_batch(&format!(
|
||||||
|
@ -279,6 +284,8 @@ pub enum MigrationError {
|
||||||
Upgrade,
|
Upgrade,
|
||||||
DeleteInfo,
|
DeleteInfo,
|
||||||
InsertInfo,
|
InsertInfo,
|
||||||
|
StartTransaction,
|
||||||
|
Db(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for MigrationError {
|
impl std::fmt::Display for MigrationError {
|
||||||
|
@ -291,13 +298,14 @@ impl std::fmt::Display for MigrationError {
|
||||||
MigrationError::InsertInfo => {
|
MigrationError::InsertInfo => {
|
||||||
f.write_str("Cannot insert a new tas information to the database")
|
f.write_str("Cannot insert a new tas information to the database")
|
||||||
}
|
}
|
||||||
|
MigrationError::Db(inner) => write!(f, "Database error: {}", inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for MigrationError {}
|
impl std::error::Error for MigrationError {}
|
||||||
|
|
||||||
const LATEST_VERSION: i64 = 202208201623;
|
const LATEST_VERSION: i64 = 202208211624;
|
||||||
|
|
||||||
impl SqliteRepo {
|
impl SqliteRepo {
|
||||||
pub fn upgrade(&self) -> Result<(), MigrationError> {
|
pub fn upgrade(&self) -> Result<(), MigrationError> {
|
||||||
|
@ -305,12 +313,50 @@ impl SqliteRepo {
|
||||||
match version {
|
match version {
|
||||||
Some(LATEST_VERSION) => return Ok(()),
|
Some(LATEST_VERSION) => return Ok(()),
|
||||||
None => {
|
None => {
|
||||||
run_migration!(self, version = LATEST_VERSION => "schema");
|
run_migration!(self, version = LATEST_VERSION, file = "schema");
|
||||||
}
|
}
|
||||||
Some(v) => {
|
_ => {
|
||||||
if v == 202208162308 {
|
if version == Some(202208162308) {
|
||||||
run_migration!(self, version = 202208201623);
|
run_migration!(self, version = 202208201623);
|
||||||
}
|
}
|
||||||
|
if version == Some(202208201623) {
|
||||||
|
run_migration!(self, version = 202208211624);
|
||||||
|
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare("SELECT id, created_at, project, name, link FROM tasks")
|
||||||
|
.map_err(|_| MigrationError::Db(Error::PrepareQuery))?;
|
||||||
|
|
||||||
|
let rows = stmt
|
||||||
|
.query_map([], |row| {
|
||||||
|
let id: i64 = row.get("id")?;
|
||||||
|
let created_at: time::OffsetDateTime = row.get("created_at")?;
|
||||||
|
let project: Option<String> = row.get("project")?;
|
||||||
|
let name: String = row.get("name")?;
|
||||||
|
let link: Option<String> = row.get("link")?;
|
||||||
|
let uuid = Uuid::new_v5(
|
||||||
|
&Uuid::NAMESPACE_OID,
|
||||||
|
format!(
|
||||||
|
"p:{},n:{},l:{},d:{}",
|
||||||
|
project.unwrap_or_default(),
|
||||||
|
name,
|
||||||
|
link.unwrap_or_default(),
|
||||||
|
created_at.unix_timestamp()
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
);
|
||||||
|
Ok((id, uuid))
|
||||||
|
})
|
||||||
|
.map_err(|_| MigrationError::Db(Error::QueryData))?;
|
||||||
|
|
||||||
|
for row in rows {
|
||||||
|
let (id, uuid) = row.map_err(|_| MigrationError::Db(Error::InvalidData))?;
|
||||||
|
|
||||||
|
this.conn
|
||||||
|
.execute("UPDATE tasks SET uuid = ?2 WHERE id = ?1", (uuid, id))
|
||||||
|
.map_err(|_| MigrationError::Db(Error::UpdateData))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue