Compare commits
3 Commits
070f002c88
...
b6adc918c6
Author | SHA1 | Date | |
---|---|---|---|
b6adc918c6 | |||
3d8867d218 | |||
fa489bf7bc |
@ -1,5 +1,4 @@
|
|||||||
use anyhow::{
|
use anyhow::{
|
||||||
anyhow,
|
|
||||||
Context,
|
Context,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
@ -9,157 +8,18 @@ use chrono::{
|
|||||||
use clap::{
|
use clap::{
|
||||||
Parser,
|
Parser,
|
||||||
};
|
};
|
||||||
use serde::{
|
use flake_tracker::{
|
||||||
Deserialize,
|
flake::{
|
||||||
|
FlakeLocksNodeInputs,
|
||||||
|
FlakeMetadata,
|
||||||
|
FlakeUri,
|
||||||
|
},
|
||||||
|
storage::{
|
||||||
|
Storage,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use sqlx::{
|
|
||||||
sqlite::SqlitePoolOptions,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct FlakeMetadata {
|
|
||||||
lastModified: i64,
|
|
||||||
locked: FlakeLockedInfo,
|
|
||||||
locks: FlakeLocks,
|
|
||||||
original: FlakeSource,
|
|
||||||
path: String,
|
|
||||||
resolved: FlakeSource,
|
|
||||||
revision: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
|
||||||
struct FlakeSource {
|
|
||||||
owner: Option<String>,
|
|
||||||
repo: Option<String>,
|
|
||||||
r#ref: Option<String>,
|
|
||||||
rev: Option<String>,
|
|
||||||
r#type: String,
|
|
||||||
url: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assemble_flake_git_uri(flake_source: &FlakeSource) -> Result<String> {
|
|
||||||
let mut out = String::new();
|
|
||||||
out.push_str("git+");
|
|
||||||
out.push_str(&flake_source.url.clone()
|
|
||||||
.context("Flake git uri does not contain an url")?);
|
|
||||||
|
|
||||||
let mut query: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
if let Some(r#ref) = &flake_source.r#ref {
|
|
||||||
query.push(format!("ref={}", r#ref));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(rev) = &flake_source.rev {
|
|
||||||
query.push(format!("rev={}", rev));
|
|
||||||
}
|
|
||||||
|
|
||||||
if query.len() > 0 {
|
|
||||||
out.push_str("?");
|
|
||||||
out.push_str(&query.join("&"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assemble_flake_github_uri(flake_source: &FlakeSource) -> Result<String> {
|
|
||||||
let mut out = String::new();
|
|
||||||
out.push_str("github:");
|
|
||||||
out.push_str(&flake_source.owner.clone()
|
|
||||||
.context("Flake github uri does not contain an owner")?);
|
|
||||||
out.push_str("/");
|
|
||||||
out.push_str(&flake_source.repo.clone()
|
|
||||||
.context("Flake github uri does not contain an repo")?);
|
|
||||||
|
|
||||||
if let Some(rev) = &flake_source.rev {
|
|
||||||
out.push_str("/");
|
|
||||||
out.push_str(&rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assemble_flake_tarball_uri(flake_source: &FlakeSource) -> Result<String> {
|
|
||||||
let mut out = String::new();
|
|
||||||
out.push_str(&flake_source.url.clone()
|
|
||||||
.context("Flake tarball uri does not contain an url")?);
|
|
||||||
|
|
||||||
if let Some(rev) = &flake_source.rev {
|
|
||||||
out.push_str("?rev=");
|
|
||||||
out.push_str(rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
trait FlakeUri {
|
|
||||||
fn flake_uri(&self) -> Result<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlakeUri for FlakeSource {
|
|
||||||
fn flake_uri(&self) -> Result<String> {
|
|
||||||
match self.r#type.as_str() {
|
|
||||||
"git" => assemble_flake_git_uri(self),
|
|
||||||
"github" => assemble_flake_github_uri(self),
|
|
||||||
"tarball" => assemble_flake_tarball_uri(self),
|
|
||||||
other => Err(anyhow!("Unsupported flake uri type {}", other)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
|
||||||
struct FlakeLockedInfo {
|
|
||||||
lastModified: i64,
|
|
||||||
narHash: String,
|
|
||||||
owner: Option<String>,
|
|
||||||
repo: Option<String>,
|
|
||||||
r#ref: Option<String>,
|
|
||||||
rev: Option<String>,
|
|
||||||
r#type: String,
|
|
||||||
url: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlakeUri for FlakeLockedInfo {
|
|
||||||
fn flake_uri(&self) -> Result<String> {
|
|
||||||
Into::<FlakeSource>::into(self).flake_uri()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<FlakeSource> for &FlakeLockedInfo {
|
|
||||||
fn into(self) -> FlakeSource {
|
|
||||||
FlakeSource {
|
|
||||||
owner: self.owner.clone(),
|
|
||||||
repo: self.repo.clone(),
|
|
||||||
r#ref: self.r#ref.clone(),
|
|
||||||
rev: self.rev.clone(),
|
|
||||||
r#type: self.r#type.clone(),
|
|
||||||
url: self.url.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct FlakeLocks {
|
|
||||||
nodes: HashMap<String, FlakeLocksNode>,
|
|
||||||
root: String,
|
|
||||||
version: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum FlakeLocksNodeInputs {
|
|
||||||
String(String),
|
|
||||||
Array(Vec<String>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct FlakeLocksNode {
|
|
||||||
inputs: Option<HashMap<String, FlakeLocksNodeInputs>>,
|
|
||||||
locked: Option<FlakeLockedInfo>,
|
|
||||||
original: Option<FlakeSource>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
@ -172,7 +32,10 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let scan_time = Utc::now().timestamp();
|
let scan_time = Utc::now().timestamp();
|
||||||
|
|
||||||
let db = SqlitePoolOptions::new().connect("sqlite://flake-tracker.db").await?;
|
let storage = Storage::connect("sqlite://flake-tracker.db")
|
||||||
|
.await
|
||||||
|
.context("Failed to connect to database")?;
|
||||||
|
|
||||||
|
|
||||||
let flake_metadata_raw = Command::new("nix")
|
let flake_metadata_raw = Command::new("nix")
|
||||||
.arg("flake")
|
.arg("flake")
|
||||||
@ -200,7 +63,7 @@ async fn main() -> Result<()> {
|
|||||||
.bind(&flake_metadata.locked.narHash)
|
.bind(&flake_metadata.locked.narHash)
|
||||||
.bind(&flake_metadata.locked.lastModified)
|
.bind(&flake_metadata.locked.lastModified)
|
||||||
.bind(&scan_time)
|
.bind(&scan_time)
|
||||||
.execute(&db).await?;
|
.execute(&storage.db).await?;
|
||||||
|
|
||||||
let locks_root_name = &flake_metadata.locks.root;
|
let locks_root_name = &flake_metadata.locks.root;
|
||||||
let locks_root_node = flake_metadata.locks.nodes.get(locks_root_name)
|
let locks_root_node = flake_metadata.locks.nodes.get(locks_root_name)
|
||||||
@ -226,7 +89,7 @@ async fn main() -> Result<()> {
|
|||||||
.bind(locks_input_node.original.clone().context("Unexpected missing lock")?.flake_uri()?)
|
.bind(locks_input_node.original.clone().context("Unexpected missing lock")?.flake_uri()?)
|
||||||
.bind(locks_input_node.locked.clone().context("Unexpected missing lock")?.narHash)
|
.bind(locks_input_node.locked.clone().context("Unexpected missing lock")?.narHash)
|
||||||
.bind(locks_input_node.locked.clone().context("Unexpected missing lock")?.lastModified)
|
.bind(locks_input_node.locked.clone().context("Unexpected missing lock")?.lastModified)
|
||||||
.execute(&db).await?;
|
.execute(&storage.db).await?;
|
||||||
|
|
||||||
sqlx::query("INSERT INTO revisions (revision_uri, flake_uri)
|
sqlx::query("INSERT INTO revisions (revision_uri, flake_uri)
|
||||||
VALUES (?, ?)
|
VALUES (?, ?)
|
||||||
@ -234,7 +97,7 @@ async fn main() -> Result<()> {
|
|||||||
")
|
")
|
||||||
.bind(locks_input_node.locked.clone().context("Unexpected missing lock")?.flake_uri()?)
|
.bind(locks_input_node.locked.clone().context("Unexpected missing lock")?.flake_uri()?)
|
||||||
.bind(locks_input_node.original.clone().context("Unexpected missing lock")?.flake_uri()?)
|
.bind(locks_input_node.original.clone().context("Unexpected missing lock")?.flake_uri()?)
|
||||||
.execute(&db).await?;
|
.execute(&storage.db).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
152
src/flake.rs
Normal file
152
src/flake.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use anyhow::{
|
||||||
|
anyhow,
|
||||||
|
Context,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
use serde::{
|
||||||
|
Deserialize,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct FlakeMetadata {
|
||||||
|
pub lastModified: i64,
|
||||||
|
pub locked: FlakeLockedInfo,
|
||||||
|
pub locks: FlakeLocks,
|
||||||
|
pub original: FlakeSource,
|
||||||
|
pub path: String,
|
||||||
|
pub resolved: FlakeSource,
|
||||||
|
pub revision: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct FlakeSource {
|
||||||
|
pub owner: Option<String>,
|
||||||
|
pub repo: Option<String>,
|
||||||
|
pub r#ref: Option<String>,
|
||||||
|
pub rev: Option<String>,
|
||||||
|
pub r#type: String,
|
||||||
|
pub url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assemble_flake_git_uri(flake_source: &FlakeSource) -> Result<String> {
|
||||||
|
let mut out = String::new();
|
||||||
|
out.push_str("git+");
|
||||||
|
out.push_str(&flake_source.url.clone()
|
||||||
|
.context("Flake git uri does not contain an url")?);
|
||||||
|
|
||||||
|
let mut query: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
if let Some(r#ref) = &flake_source.r#ref {
|
||||||
|
query.push(format!("ref={}", r#ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rev) = &flake_source.rev {
|
||||||
|
query.push(format!("rev={}", rev));
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.len() > 0 {
|
||||||
|
out.push_str("?");
|
||||||
|
out.push_str(&query.join("&"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assemble_flake_github_uri(flake_source: &FlakeSource) -> Result<String> {
|
||||||
|
let mut out = String::new();
|
||||||
|
out.push_str("github:");
|
||||||
|
out.push_str(&flake_source.owner.clone()
|
||||||
|
.context("Flake github uri does not contain an owner")?);
|
||||||
|
out.push_str("/");
|
||||||
|
out.push_str(&flake_source.repo.clone()
|
||||||
|
.context("Flake github uri does not contain an repo")?);
|
||||||
|
|
||||||
|
if let Some(rev) = &flake_source.rev {
|
||||||
|
out.push_str("/");
|
||||||
|
out.push_str(&rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assemble_flake_tarball_uri(flake_source: &FlakeSource) -> Result<String> {
|
||||||
|
let mut out = String::new();
|
||||||
|
out.push_str(&flake_source.url.clone()
|
||||||
|
.context("Flake tarball uri does not contain an url")?);
|
||||||
|
|
||||||
|
if let Some(rev) = &flake_source.rev {
|
||||||
|
out.push_str("?rev=");
|
||||||
|
out.push_str(rev);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FlakeUri {
|
||||||
|
fn flake_uri(&self) -> Result<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlakeUri for FlakeSource {
|
||||||
|
fn flake_uri(&self) -> Result<String> {
|
||||||
|
match self.r#type.as_str() {
|
||||||
|
"git" => assemble_flake_git_uri(self),
|
||||||
|
"github" => assemble_flake_github_uri(self),
|
||||||
|
"tarball" => assemble_flake_tarball_uri(self),
|
||||||
|
other => Err(anyhow!("Unsupported flake uri type {}", other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct FlakeLockedInfo {
|
||||||
|
pub lastModified: i64,
|
||||||
|
pub narHash: String,
|
||||||
|
pub owner: Option<String>,
|
||||||
|
pub repo: Option<String>,
|
||||||
|
pub r#ref: Option<String>,
|
||||||
|
pub rev: Option<String>,
|
||||||
|
pub r#type: String,
|
||||||
|
pub url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlakeUri for FlakeLockedInfo {
|
||||||
|
fn flake_uri(&self) -> Result<String> {
|
||||||
|
Into::<FlakeSource>::into(self).flake_uri()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<FlakeSource> for &FlakeLockedInfo {
|
||||||
|
fn into(self) -> FlakeSource {
|
||||||
|
FlakeSource {
|
||||||
|
owner: self.owner.clone(),
|
||||||
|
repo: self.repo.clone(),
|
||||||
|
r#ref: self.r#ref.clone(),
|
||||||
|
rev: self.rev.clone(),
|
||||||
|
r#type: self.r#type.clone(),
|
||||||
|
url: self.url.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct FlakeLocks {
|
||||||
|
pub nodes: HashMap<String, FlakeLocksNode>,
|
||||||
|
pub root: String,
|
||||||
|
pub version: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum FlakeLocksNodeInputs {
|
||||||
|
String(String),
|
||||||
|
Array(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct FlakeLocksNode {
|
||||||
|
pub inputs: Option<HashMap<String, FlakeLocksNodeInputs>>,
|
||||||
|
pub locked: Option<FlakeLockedInfo>,
|
||||||
|
pub original: Option<FlakeSource>,
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
|||||||
|
pub mod flake;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod templates;
|
pub mod templates;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
@ -28,7 +28,7 @@ impl Storage {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn flakes(&self) -> Result<Vec<FlakeUri>> {
|
pub async fn flakes(&self) -> Result<Vec<FlakeRow>> {
|
||||||
sqlx::query_as("
|
sqlx::query_as("
|
||||||
SELECT
|
SELECT
|
||||||
flake_uri
|
flake_uri
|
||||||
@ -40,11 +40,15 @@ impl Storage {
|
|||||||
.context("Failed to fetch data from database")
|
.context("Failed to fetch data from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn revisions_from_flake(&self, uri: &str) -> Result<Vec<RevisionListModel>> {
|
pub async fn revisions_from_flake(&self, uri: &str) -> Result<Vec<RevisionRow>> {
|
||||||
sqlx::query_as("
|
sqlx::query_as("
|
||||||
SELECT
|
SELECT
|
||||||
revision_uri,
|
revision_uri,
|
||||||
last_modified
|
flake_uri,
|
||||||
|
nix_store_path,
|
||||||
|
nar_hash,
|
||||||
|
last_modified,
|
||||||
|
tracker_last_scanned
|
||||||
FROM revisions
|
FROM revisions
|
||||||
WHERE flake_uri = ?
|
WHERE flake_uri = ?
|
||||||
ORDER BY last_modified DESC
|
ORDER BY last_modified DESC
|
||||||
@ -55,7 +59,7 @@ impl Storage {
|
|||||||
.context("Failed to fetch data from database")
|
.context("Failed to fetch data from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn inputs_for_revision(&self, revision_uri: &str) -> Result<Vec<InputModel>> {
|
pub async fn inputs_for_revision(&self, revision_uri: &str) -> Result<Vec<InputRow>> {
|
||||||
sqlx::query_as("
|
sqlx::query_as("
|
||||||
SELECT
|
SELECT
|
||||||
revision_uri,
|
revision_uri,
|
||||||
@ -74,7 +78,7 @@ impl Storage {
|
|||||||
.context("Failed to fetch data from database")
|
.context("Failed to fetch data from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn input_of_for_revision(&self, revision_uri: &str) -> Result<Vec<InputModel>> {
|
pub async fn input_of_for_revision(&self, revision_uri: &str) -> Result<Vec<InputRow>> {
|
||||||
sqlx::query_as("
|
sqlx::query_as("
|
||||||
SELECT
|
SELECT
|
||||||
revision_uri,
|
revision_uri,
|
||||||
@ -93,14 +97,17 @@ impl Storage {
|
|||||||
.context("Failed to fetch data from database")
|
.context("Failed to fetch data from database")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn current_inputs_for_flake(&self, flake_uri: &str) -> Result<Vec<InputForFlakeRow>> {
|
pub async fn current_inputs_for_flake(&self, flake_uri: &str) -> Result<Vec<InputRow>> {
|
||||||
sqlx::query_as("
|
sqlx::query_as("
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
revisions.revision_uri,
|
revisions.revision_uri,
|
||||||
MAX(revisions.last_modified) AS last_modified,
|
|
||||||
inputs.input_name,
|
inputs.input_name,
|
||||||
inputs.locked_flake_uri
|
inputs.locked_revision_uri,
|
||||||
|
inputs.locked_flake_uri,
|
||||||
|
inputs.locked_nar_hash,
|
||||||
|
inputs.last_modified,
|
||||||
|
MAX(revisions.last_modified)
|
||||||
FROM
|
FROM
|
||||||
revisions
|
revisions
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
@ -120,57 +127,30 @@ impl Storage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromRow)]
|
#[derive(FromRow)]
|
||||||
pub struct FlakeRevisionRow {
|
pub struct RevisionRow {
|
||||||
pub revision_uri: String,
|
pub revision_uri: String,
|
||||||
pub flake_uri: Option<String>,
|
pub flake_uri: Option<String>,
|
||||||
pub nix_store_path: Option<String>,
|
pub nix_store_path: Option<String>,
|
||||||
pub nar_hash: Option<String>,
|
pub nar_hash: Option<String>,
|
||||||
pub last_modified: Option<i64>,
|
pub last_modified: Option<i64>,
|
||||||
|
pub tracker_last_scanned: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromRow)]
|
impl RevisionRow {
|
||||||
pub struct InputForFlakeRow {
|
pub fn revision_link(&self) -> String {
|
||||||
pub revision_uri: String,
|
format!("/r/{}", urlencode(&self.revision_uri))
|
||||||
pub last_modified: Option<i64>,
|
}
|
||||||
pub input_name: String,
|
|
||||||
pub locked_flake_uri: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputForFlakeRow {
|
pub fn flake_link(&self) -> String {
|
||||||
pub fn locked_flake_link(&self ) -> String {
|
match &self.flake_uri {
|
||||||
match &self.locked_flake_uri {
|
Some(flake_uri) => format!("/f/{}", urlencode(&flake_uri)),
|
||||||
Some(locked_flake_uri) => format!("/f/{}", urlencode(&locked_flake_uri)),
|
|
||||||
None => String::from("#"),
|
None => String::from("#"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(FromRow)]
|
#[derive(FromRow)]
|
||||||
pub struct RevisionListModel {
|
pub struct InputRow {
|
||||||
pub revision_uri: String,
|
|
||||||
pub last_modified: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RevisionListModel {
|
|
||||||
pub fn revision_link(&self ) -> String {
|
|
||||||
format!("/r/{}", urlencode(&self.revision_uri))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromRow)]
|
|
||||||
pub struct FlakeUri {
|
|
||||||
pub flake_uri: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlakeUri {
|
|
||||||
pub fn flake_link(&self ) -> String {
|
|
||||||
format!("/f/{}", urlencode(&self.flake_uri))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromRow)]
|
|
||||||
pub struct InputModel {
|
|
||||||
pub revision_uri: String,
|
pub revision_uri: String,
|
||||||
pub input_name: String,
|
pub input_name: String,
|
||||||
pub locked_revision_uri: Option<String>,
|
pub locked_revision_uri: Option<String>,
|
||||||
@ -179,7 +159,7 @@ pub struct InputModel {
|
|||||||
pub last_modified: Option<i64>,
|
pub last_modified: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputModel {
|
impl InputRow {
|
||||||
pub fn revision_link(&self) -> String {
|
pub fn revision_link(&self) -> String {
|
||||||
format!("/r/{}", urlencode(&self.revision_uri))
|
format!("/r/{}", urlencode(&self.revision_uri))
|
||||||
}
|
}
|
||||||
@ -198,3 +178,15 @@ impl InputModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromRow)]
|
||||||
|
pub struct FlakeRow {
|
||||||
|
pub flake_uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlakeRow {
|
||||||
|
pub fn flake_link(&self ) -> String {
|
||||||
|
format!("/f/{}", urlencode(&self.flake_uri))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,9 @@ use askama::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
storage::{
|
storage::{
|
||||||
FlakeUri,
|
FlakeRow,
|
||||||
InputModel,
|
InputRow,
|
||||||
InputForFlakeRow,
|
RevisionRow,
|
||||||
RevisionListModel,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -18,21 +17,21 @@ pub struct IndexTemplate {
|
|||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "flakes.html")]
|
#[template(path = "flakes.html")]
|
||||||
pub struct FlakesTemplate {
|
pub struct FlakesTemplate {
|
||||||
pub flakes: Vec<FlakeUri>,
|
pub flakes: Vec<FlakeRow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "flake.html")]
|
#[template(path = "flake.html")]
|
||||||
pub struct FlakeTemplate {
|
pub struct FlakeTemplate {
|
||||||
pub uri: String,
|
pub uri: String,
|
||||||
pub revisions: Vec<RevisionListModel>,
|
pub revisions: Vec<RevisionRow>,
|
||||||
pub current_inputs: Vec<InputForFlakeRow>,
|
pub current_inputs: Vec<InputRow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "revision.html")]
|
#[template(path = "revision.html")]
|
||||||
pub struct RevisionTemplate {
|
pub struct RevisionTemplate {
|
||||||
pub revision_uri: String,
|
pub revision_uri: String,
|
||||||
pub inputs: Vec<InputModel>,
|
pub inputs: Vec<InputRow>,
|
||||||
pub input_of: Vec<InputModel>,
|
pub input_of: Vec<InputRow>,
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{% for revision in revisions %}
|
{% for revision in revisions %}
|
||||||
<li><a href="{{ revision.revision_link() }}">{{ revision.revision_uri }}</a> ({{ revision.last_modified }})</li>
|
<li><a href="{{ revision.revision_link() }}">{{ revision.revision_uri }}</a> {% match revision.last_modified %}{% when Some with (last_modified) %}({{ last_modified }}){% when None %}{% endmatch %}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user