Build css with rust. Fix letter avatar
This commit is contained in:
parent
7c5b63fa2c
commit
45d356aa92
1
jirs-client/.gitignore
vendored
1
jirs-client/.gitignore
vendored
@ -2,3 +2,4 @@ pkg
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
.yarn-error.log
|
.yarn-error.log
|
||||||
|
tmp
|
||||||
|
@ -148,12 +148,6 @@
|
|||||||
width: 90px;
|
width: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
|
||||||
#projectPage > #projectBoardLists > .list > .issues > .issueLink > .issue {
|
|
||||||
padding: 10px 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#projectPage > #projectBoardLists > .list > .issues > .issueLink > .issue:hover {
|
#projectPage > #projectBoardLists > .list > .issues > .issueLink > .issue:hover {
|
||||||
background: var(--backgroundLight);
|
background: var(--backgroundLight);
|
||||||
}
|
}
|
||||||
@ -164,6 +158,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
|
#projectPage > #projectBoardLists > .list > .issues > .issueLink > .issue {
|
||||||
|
padding: 10px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#projectPage > #projectBoardLists > .list > .issues > .issueLink > .issue > .title {
|
#projectPage > #projectBoardLists > .list > .issues > .issueLink > .issue > .title {
|
||||||
font-size: 14.5px
|
font-size: 14.5px
|
||||||
}
|
}
|
||||||
|
@ -27,3 +27,35 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor1, .styledAvatar span.avatarColor1 {
|
||||||
|
color: var(--avatar-color-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor2, .styledAvatar span.avatarColor2 {
|
||||||
|
color: var(--avatar-color-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor3, .styledAvatar span.avatarColor3 {
|
||||||
|
color: var(--avatar-color-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor4, .styledAvatar span.avatarColor4 {
|
||||||
|
color: var(--avatar-color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor5, .styledAvatar span.avatarColor5 {
|
||||||
|
color: var(--avatar-color-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor6, .styledAvatar span.avatarColor6 {
|
||||||
|
color: var(--avatar-color-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor7, .styledAvatar span.avatarColor7 {
|
||||||
|
color: var(--avatar-color-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledAvatar.avatarColor8, .styledAvatar span.avatarColor8 {
|
||||||
|
color: var(--avatar-color-8);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import "./styles.css";
|
import "../tmp/styles.css";
|
||||||
|
|
||||||
const getWsHostName = () => process.env.JIRS_SERVER_BIND === "0.0.0.0" ? 'localhost' : process.env.JIRS_SERVER_BIND;
|
const getWsHostName = () => process.env.JIRS_SERVER_BIND === "0.0.0.0" ? 'localhost' : process.env.JIRS_SERVER_BIND;
|
||||||
const getProtocol = () => window.location.protocol.replace(/^http/, 'ws');
|
const getProtocol = () => window.location.protocol.replace(/^http/, 'ws');
|
||||||
|
@ -228,7 +228,8 @@ fn avatars_filters(model: &Model) -> Node<Msg> {
|
|||||||
let avatars: Vec<Node<Msg>> = model
|
let avatars: Vec<Node<Msg>> = model
|
||||||
.users
|
.users
|
||||||
.iter()
|
.iter()
|
||||||
.map(|user| {
|
.enumerate()
|
||||||
|
.map(|(idx, user)| {
|
||||||
let mut class_list = vec!["avatarIsActiveBorder"];
|
let mut class_list = vec!["avatarIsActiveBorder"];
|
||||||
let user_id = user.id;
|
let user_id = user.id;
|
||||||
let active = active_avatar_filters.contains(&user_id);
|
let active = active_avatar_filters.contains(&user_id);
|
||||||
@ -241,6 +242,7 @@ fn avatars_filters(model: &Model) -> Node<Msg> {
|
|||||||
Msg::ProjectAvatarFilterChanged(user_id, active)
|
Msg::ProjectAvatarFilterChanged(user_id, active)
|
||||||
}))
|
}))
|
||||||
.name(user.name.as_str())
|
.name(user.name.as_str())
|
||||||
|
.user_index(idx)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
div![attrs![At::Class => class_list.join(" ")], styled_avatar]
|
div![attrs![At::Class => class_list.join(" ")], styled_avatar]
|
||||||
@ -345,12 +347,14 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
|||||||
let avatars: Vec<Node<Msg>> = model
|
let avatars: Vec<Node<Msg>> = model
|
||||||
.users
|
.users
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|user| issue.user_ids.contains(&user.id))
|
.enumerate()
|
||||||
.map(|user| {
|
.filter(|(_, user)| issue.user_ids.contains(&user.id))
|
||||||
|
.map(|(idx, user)| {
|
||||||
StyledAvatar::build()
|
StyledAvatar::build()
|
||||||
.size(24)
|
.size(24)
|
||||||
.name(user.name.as_str())
|
.name(user.name.as_str())
|
||||||
.avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default())
|
.avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default())
|
||||||
|
.user_index(idx)
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
})
|
})
|
||||||
|
@ -9,6 +9,7 @@ pub struct StyledAvatar {
|
|||||||
name: String,
|
name: String,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<String>,
|
||||||
|
user_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for StyledAvatar {
|
impl Default for StyledAvatar {
|
||||||
@ -19,6 +20,7 @@ impl Default for StyledAvatar {
|
|||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
|
user_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,6 +33,7 @@ impl StyledAvatar {
|
|||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
on_click: None,
|
on_click: None,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
|
user_index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,6 +50,7 @@ pub struct StyledAvatarBuilder {
|
|||||||
name: String,
|
name: String,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<String>,
|
||||||
|
user_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledAvatarBuilder {
|
impl StyledAvatarBuilder {
|
||||||
@ -87,6 +91,11 @@ impl StyledAvatarBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn user_index(mut self, user_index: usize) -> Self {
|
||||||
|
self.user_index = user_index;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledAvatar {
|
pub fn build(self) -> StyledAvatar {
|
||||||
StyledAvatar {
|
StyledAvatar {
|
||||||
avatar_url: self.avatar_url,
|
avatar_url: self.avatar_url,
|
||||||
@ -94,6 +103,7 @@ impl StyledAvatarBuilder {
|
|||||||
name: self.name,
|
name: self.name,
|
||||||
on_click: self.on_click,
|
on_click: self.on_click,
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
|
user_index: self.user_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,8 +115,11 @@ pub fn render(values: StyledAvatar) -> Node<Msg> {
|
|||||||
name,
|
name,
|
||||||
on_click,
|
on_click,
|
||||||
mut class_list,
|
mut class_list,
|
||||||
|
user_index,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
|
let index = user_index % 8;
|
||||||
|
|
||||||
class_list.push("styledAvatar".to_string());
|
class_list.push("styledAvatar".to_string());
|
||||||
match avatar_url {
|
match avatar_url {
|
||||||
Some(_) => class_list.push("image".to_string()),
|
Some(_) => class_list.push("image".to_string()),
|
||||||
@ -118,15 +131,37 @@ pub fn render(values: StyledAvatar) -> Node<Msg> {
|
|||||||
None => vec![],
|
None => vec![],
|
||||||
Some(h) => vec![h],
|
Some(h) => vec![h],
|
||||||
};
|
};
|
||||||
|
let letter = name
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.last()
|
||||||
|
.map(|c| c.to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
match avatar_url {
|
match avatar_url {
|
||||||
Some(url) => div![
|
Some(url) => {
|
||||||
attrs![At::Class => class_list.join(" "), At::Style => format!("{shared}; background-image: url({url});", shared = shared_style, url = url)],
|
let style = format!(
|
||||||
handler,
|
"{shared}; background-image: url({url});",
|
||||||
],
|
shared = shared_style,
|
||||||
_ => div![
|
url = url
|
||||||
attrs![At::Class => class_list.join(" "), At::Style => shared_style],
|
);
|
||||||
span![name],
|
div![
|
||||||
handler
|
attrs![At::Class => class_list.join(" "), At::Style => style],
|
||||||
],
|
handler,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let style = format!(
|
||||||
|
"{shared}; width: {size}px; height: {size}px; font-size: calc({size}px / 1.7);",
|
||||||
|
shared = shared_style,
|
||||||
|
size = size
|
||||||
|
);
|
||||||
|
class_list.push("letter".to_string());
|
||||||
|
class_list.push(format!("avatarColor{}", index + 1));
|
||||||
|
div![
|
||||||
|
attrs![At::Class => class_list.join(" "), At::Style => style],
|
||||||
|
span![letter],
|
||||||
|
handler,
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,15 @@ const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
|||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
const { execSync, exec } = require('child_process');
|
||||||
|
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
|
||||||
process.env.RUST_LOG = 'info';
|
process.env.RUST_LOG = 'info';
|
||||||
|
|
||||||
|
execSync('cd .. && cargo build --bin jirs-css');
|
||||||
|
exec('cd .. && ./target/debug/jirs-css -O ./jirs-client/tmp/styles.css');
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -1,85 +1,202 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs::*;
|
use std::fs::*;
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
use std::sync::{Arc, RwLock, RwLockWriteGuard};
|
||||||
|
use std::time::Duration;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
||||||
|
|
||||||
const INPUT: &str = "./jirs-client/js/styles.css";
|
const INPUT: &str = "./jirs-client/js/styles.css";
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
type Css = Arc<RwLock<CssFile>>;
|
||||||
struct Configuration {
|
|
||||||
input: String,
|
#[derive(Debug)]
|
||||||
output: Option<String>,
|
enum Partial {
|
||||||
watch: bool,
|
String(String),
|
||||||
prelude_selector: bool,
|
File(Css),
|
||||||
files: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
#[derive(Debug)]
|
||||||
pub fn scan_fs(&mut self) -> Result<(), String> {
|
enum FileState {
|
||||||
let input_dir = Path::new(self.input.as_str())
|
Clean,
|
||||||
.parent()
|
Dirty,
|
||||||
.ok_or_else(|| format!("Not a valid path {:?}", self.input))?;
|
Dead,
|
||||||
|
}
|
||||||
|
|
||||||
let path = input_dir.to_str().unwrap();
|
#[derive(Debug)]
|
||||||
let paths =
|
struct CssFile {
|
||||||
glob::glob(format!("{}/**/*.css", path).as_str()).map_err(|e| format!("{}", e))?;
|
pub path: String,
|
||||||
for path in paths.filter_map(Result::ok) {
|
pub lines: Vec<Partial>,
|
||||||
self.files.push(path.display().to_string());
|
pub last_changed: SystemTime,
|
||||||
|
pub state: FileState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CssFile {
|
||||||
|
pub fn new(path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
lines: vec![],
|
||||||
|
last_changed: SystemTime::UNIX_EPOCH,
|
||||||
|
state: FileState::Clean,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CssFile {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(format!("\n/* -- {} --- */\n\n", self.path).as_str())?;
|
||||||
|
for line in self.lines.iter() {
|
||||||
|
match line {
|
||||||
|
Partial::String(line) => {
|
||||||
|
f.write_str(line.as_str())?;
|
||||||
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
Partial::File(file) => {
|
||||||
|
if let Ok(css) = file.read() {
|
||||||
|
f.write_str(format!("{}", css).as_str())?;
|
||||||
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_files(input: &Path, out: &mut Vec<String>) -> Result<(), String> {
|
#[derive(Debug, Default)]
|
||||||
let input_dir = input
|
struct Application {
|
||||||
.clone()
|
input: String,
|
||||||
.parent()
|
output: Option<String>,
|
||||||
.ok_or_else(|| format!("Not a valid path {:?}", input))?;
|
watch: bool,
|
||||||
let contents: String =
|
prelude_selector: bool,
|
||||||
read_to_string(input).map_err(|_| format!("File cannot be read {:?}", input))?;
|
files_map: HashMap<String, HashSet<String>>,
|
||||||
|
fm: HashMap<String, Css>,
|
||||||
|
root_file: Option<Css>,
|
||||||
|
}
|
||||||
|
|
||||||
for line in contents.lines() {
|
impl Application {
|
||||||
if line.trim().is_empty() {
|
fn read_timestamp(input: &Path) -> Result<SystemTime, String> {
|
||||||
continue;
|
std::fs::File::open(input)
|
||||||
|
.and_then(|file| file.metadata())
|
||||||
|
.and_then(|meta| meta.modified())
|
||||||
|
.map_err(|e| format!("{}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_timestamps(
|
||||||
|
&mut self,
|
||||||
|
input: &Path,
|
||||||
|
output_timestamp: SystemTime,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
let input_dir = input
|
||||||
|
.clone()
|
||||||
|
.parent()
|
||||||
|
.ok_or_else(|| format!("Not a valid path {:?}", input))?;
|
||||||
|
|
||||||
|
let path = input_dir.to_str().unwrap();
|
||||||
|
let paths =
|
||||||
|
glob::glob(format!("{}/**/*.css", path).as_str()).map_err(|e| format!("{}", e))?;
|
||||||
|
for path in paths.filter_map(Result::ok) {
|
||||||
|
if Self::read_timestamp(path.as_path())? > output_timestamp {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !line.starts_with("@import ") {
|
Ok(true)
|
||||||
out.push(line.to_string());
|
}
|
||||||
continue;
|
|
||||||
|
fn parse(&mut self) -> Result<(), String> {
|
||||||
|
let root_path = self.input.to_string();
|
||||||
|
let root = std::path::Path::new(&root_path);
|
||||||
|
let root_file = self.parse_file(root)?;
|
||||||
|
self.root_file = Some(root_file);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_file(&mut self, input: &Path) -> Result<Css, String> {
|
||||||
|
let file_path = input.display().to_string();
|
||||||
|
let input_dir = input
|
||||||
|
.clone()
|
||||||
|
.parent()
|
||||||
|
.ok_or_else(|| format!("Not a valid path {:?}", input))?;
|
||||||
|
let file = self
|
||||||
|
.fm
|
||||||
|
.entry(file_path.clone())
|
||||||
|
.or_insert_with(|| Arc::new(RwLock::new(CssFile::new(file_path.clone()))))
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
if let Ok(mut css) = file.write() {
|
||||||
|
css.last_changed = Self::read_timestamp(input)?;
|
||||||
}
|
}
|
||||||
let imported = {
|
|
||||||
line.replace("@import ", "")
|
for line in read_to_string(file_path.as_str())
|
||||||
.trim()
|
.map_err(|e| format!("{}", e))?
|
||||||
.replace("\"", "")
|
.lines()
|
||||||
.replace(";", "")
|
{
|
||||||
.to_string()
|
let l = line.trim();
|
||||||
|
match l {
|
||||||
|
"" => continue,
|
||||||
|
_ if l.starts_with("@import ") => {
|
||||||
|
let imported = line
|
||||||
|
.replace("@import ", "")
|
||||||
|
.trim()
|
||||||
|
.replace("\"", "")
|
||||||
|
.replace(";", "")
|
||||||
|
.to_string();
|
||||||
|
let child = input_dir
|
||||||
|
.clone()
|
||||||
|
.join(imported.as_str())
|
||||||
|
.canonicalize()
|
||||||
|
.map_err(|e| format!("{}", e))?;
|
||||||
|
let child_file = self.parse_file(&child)?;
|
||||||
|
|
||||||
|
if let Ok(mut css) = file.write() {
|
||||||
|
css.lines.push(Partial::File(child_file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Ok(mut css) = file.write() {
|
||||||
|
css.lines.push(Partial::String(l.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_dirty(&mut self, path: &Path) {
|
||||||
|
if let Ok(mut css) = self.css_at_path(path) {
|
||||||
|
css.state = FileState::Dirty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_dead(&mut self, path: &Path) {
|
||||||
|
if let Ok(mut css) = self.css_at_path(path) {
|
||||||
|
css.state = FileState::Dead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn css_at_path(&mut self, path: &Path) -> Result<RwLockWriteGuard<CssFile>, bool> {
|
||||||
|
self.fm
|
||||||
|
.get(path.display().to_string().as_str())
|
||||||
|
.ok_or(false)
|
||||||
|
.and_then(|css| css.write().or(Err(false)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print(&self) {
|
||||||
|
let css = match self.root_file.as_ref().unwrap().read() {
|
||||||
|
Ok(css) => css,
|
||||||
|
_ => return,
|
||||||
};
|
};
|
||||||
let child = input_dir.clone().join(imported.as_str());
|
match self.output.as_ref() {
|
||||||
merge_files(child.as_path(), out)?;
|
Some(f) => {
|
||||||
}
|
std::fs::create_dir_all(Path::new(f).parent().unwrap()).unwrap();
|
||||||
Ok(())
|
std::fs::write(f, format!("{}", css)).unwrap();
|
||||||
}
|
println!("CSS merge done");
|
||||||
|
}
|
||||||
fn read_timestamp(input: &Path) -> Result<SystemTime, String> {
|
_ => println!("{}", css),
|
||||||
std::fs::File::open(input)
|
|
||||||
.and_then(|file| file.metadata())
|
|
||||||
.and_then(|meta| meta.modified())
|
|
||||||
.map_err(|e| format!("{}", e))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_timestamps(input: &Path, output_timestamp: SystemTime) -> Result<bool, String> {
|
|
||||||
let input_dir = input
|
|
||||||
.clone()
|
|
||||||
.parent()
|
|
||||||
.ok_or_else(|| format!("Not a valid path {:?}", input))?;
|
|
||||||
|
|
||||||
let path = input_dir.to_str().unwrap();
|
|
||||||
let paths = glob::glob(format!("{}/**/*.css", path).as_str()).map_err(|e| format!("{}", e))?;
|
|
||||||
for path in paths.filter_map(Result::ok) {
|
|
||||||
if read_timestamp(path.as_path())? > output_timestamp {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
@ -99,15 +216,17 @@ fn main() -> Result<(), String> {
|
|||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut config = Configuration {
|
let mut app = Application {
|
||||||
input: matches.value_of("input").unwrap().to_string(),
|
input: matches.value_of("input").unwrap().to_string(),
|
||||||
output: matches.value_of("output").map(|s| s.to_string()),
|
output: matches.value_of("output").map(|s| s.to_string()),
|
||||||
watch: matches.is_present("watch"),
|
watch: matches.is_present("watch"),
|
||||||
prelude_selector: matches.is_present("prelude"),
|
prelude_selector: matches.is_present("prelude"),
|
||||||
files: vec![],
|
files_map: Default::default(),
|
||||||
|
fm: Default::default(),
|
||||||
|
root_file: None,
|
||||||
};
|
};
|
||||||
config.scan_fs()?;
|
let root_path = app.input.to_string();
|
||||||
println!("{:?}", config);
|
let root = std::path::Path::new(&root_path);
|
||||||
|
|
||||||
let output_timestamp = matches
|
let output_timestamp = matches
|
||||||
.value_of("output")
|
.value_of("output")
|
||||||
@ -117,28 +236,37 @@ fn main() -> Result<(), String> {
|
|||||||
.and_then(|meta| meta.modified())
|
.and_then(|meta| meta.modified())
|
||||||
.unwrap_or_else(|_| SystemTime::UNIX_EPOCH.clone());
|
.unwrap_or_else(|_| SystemTime::UNIX_EPOCH.clone());
|
||||||
|
|
||||||
let mut file =
|
if app.check_timestamps(root, output_timestamp)? {
|
||||||
std::fs::File::open(matches.value_of("input").unwrap()).map_err(|e| format!("{}", e))?;
|
|
||||||
let input = matches.value_of("input").unwrap();
|
|
||||||
|
|
||||||
if check_timestamps(Path::new(input), output_timestamp)? {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut contents: String = String::new();
|
app.parse()?;
|
||||||
file.read_to_string(&mut contents)
|
app.print();
|
||||||
.map_err(|e| format!("{}", e))?;
|
|
||||||
|
|
||||||
let mut out = vec![];
|
let (tx, rx) = channel();
|
||||||
merge_files(std::path::Path::new(input), &mut out)?;
|
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||||
|
|
||||||
match matches.value_of("output") {
|
for file in app.fm.keys() {
|
||||||
Some(output) => {
|
watcher
|
||||||
std::fs::create_dir_all(Path::new(output).parent().unwrap()).unwrap();
|
.watch(file.to_string(), RecursiveMode::NonRecursive)
|
||||||
std::fs::write(output, out.join("\n")).unwrap()
|
.unwrap();
|
||||||
}
|
|
||||||
None => println!("{}", out.join("\n")),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
loop {
|
||||||
|
match rx.recv() {
|
||||||
|
Ok(DebouncedEvent::NoticeWrite(path)) => {
|
||||||
|
app.mark_dirty(path.as_path());
|
||||||
|
if let Err(s) = app.parse_file(&path) {
|
||||||
|
eprintln!("{}", s);
|
||||||
|
}
|
||||||
|
app.print();
|
||||||
|
}
|
||||||
|
Ok(DebouncedEvent::NoticeRemove(path)) => {
|
||||||
|
app.mark_dead(path.as_path());
|
||||||
|
watcher.unwatch(path).unwrap();
|
||||||
|
}
|
||||||
|
Ok(event) => println!("{:?}", event),
|
||||||
|
Err(e) => println!("watch error: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user