From 50a35a7a5b9abae2a7df0d489441fa6b44f9c5c2 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 30 Mar 2021 02:37:34 +0100 Subject: [PATCH] Use new Html everywhere Signed-off-by: Ian Jackson --- apitest/at-otter.rs | 4 ++- daemon/api.rs | 10 +++--- daemon/cmdlistener.rs | 76 +++++++++++++++++++++--------------------- daemon/session.rs | 6 ++-- src/accounts.rs | 1 + src/bin/otterlib.rs | 29 +++++++++++----- src/clock.rs | 77 +++++++++++++++++++++++-------------------- src/deck.rs | 38 ++++++++++----------- src/gamestate.rs | 28 ++-------------- src/global.rs | 25 +++++++------- src/hand.rs | 24 +++++++------- src/hidden.rs | 18 +++++----- src/keydata.rs | 1 + src/lib.rs | 1 + src/pcrender.rs | 23 ++++++------- src/pieces.rs | 64 ++++++++++++++++++----------------- src/prelude.rs | 2 ++ src/shapelib-toml.rs | 5 +-- src/shapelib.rs | 28 ++++++++-------- src/spec.rs | 6 ++-- src/ui.rs | 2 +- src/updates.rs | 30 ++++++++--------- wdriver/wdt-hand.rs | 4 +-- 23 files changed, 255 insertions(+), 247 deletions(-) diff --git a/apitest/at-otter.rs b/apitest/at-otter.rs index a9ea0de0..4b8e37fe 100644 --- a/apitest/at-otter.rs +++ b/apitest/at-otter.rs @@ -537,7 +537,9 @@ impl Ctx { // ----- alice: claim alices' hand ----- let [hand] = a_pieces.iter_enumerated() - .filter(|(_i,p)| p.info["desc"] == otter::hand::UNCLAIMED_DESC) + .filter(|(_i,p)| { + p.info["desc"] == otter::hand::UNCLAIMED_DESC.as_html_str() + }) .map(|(i,_)| i) .collect::>() .into_inner().unwrap(); diff --git a/daemon/api.rs b/daemon/api.rs index 54320492..902b36f1 100644 --- a/daemon/api.rs +++ b/daemon/api.rs @@ -319,7 +319,7 @@ api_route!{ let pri = piece_pri(ioccults, &gs.occults, player, gpl, piece, gpc, ipc) .ok_or(POE::PieceGone)?; - let pcs = pri.describe(ioccults,&gs.occults, gpc, ipc).0; + let pcs = pri.describe(ioccults,&gs.occults, gpc, ipc); gpc.held = Some(player); @@ -327,10 +327,10 @@ api_route!{ let pls = &htmlescape::encode_minimal(&gpl.nick); - let logent = LogEntry { html: Html(match was { - Some(was) => format!("{} wrested {} from {}", pls, pcs, was), - None => format!("{} wrested {}", pls, pcs), - })}; + let logent = LogEntry { html: match was { + Some(was) => hformat!("{} wrested {} from {}", pls, pcs, was), + None => hformat!("{} wrested {}", pls, pcs), + }}; (WhatResponseToClientOp::Predictable, update, vec![logent]).into() diff --git a/daemon/cmdlistener.rs b/daemon/cmdlistener.rs index 5ef042a0..afc698e2 100644 --- a/daemon/cmdlistener.rs +++ b/daemon/cmdlistener.rs @@ -159,7 +159,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { let auth = authorise_by_account(cs, &ag, &game)?; let gs = otter::gamestate::GameState { - table_colour: Html::lit("green"), + table_colour: Html::lit("green").into(), table_size: DEFAULT_TABLE_SIZE, pieces: default(), players: default(), @@ -350,8 +350,8 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( ig.gs.table_size = size; (U{ pcs: vec![], log: vec![ LogEntry { - html: Html(format!("{} resized the table to {}x{}", - &who.0, size.x(), size.y())), + html: hformat!("{} resized the table to {}x{}", + who, size.x(), size.y()), }], raw: Some(vec![ PreparedUpdateEntry::SetTableSize(size) ]) }, Fine, None, ig) @@ -363,8 +363,8 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( ig.gs.table_colour = colour.clone(); (U{ pcs: vec![], log: vec![ LogEntry { - html: Html(format!("{} recoloured the tabletop to {}", - &who.0, &colour.0)), + html: hformat!("{} recoloured the tabletop to {}", + &who, &colour), }], raw: Some(vec![ PreparedUpdateEntry::SetTableColour(colour) ]) }, Fine, None, ig) @@ -378,7 +378,7 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( let (ig, auth) = cs.check_acl(ag, ig, PCH::Instance, &[TP::Play])?; let nick = nick.ok_or(ME::MustSpecifyNick)?; let logentry = LogEntry { - html: Html(format!("{} [{}] joined the game", &nick, &account)), + html: hformat!("{} [{}] joined the game", nick, account), }; let timezone = &arecord.timezone; let tz = tz_from_str(&timezone); @@ -482,10 +482,8 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( ig.check_new_nick(&new_nick)?; let gpl = ig.gs.players.byid_mut(player)?; log.push(LogEntry { - html: Html(format!("{} changed {}'s nick to {}", - &who.0, - htmlescape::encode_minimal(&gpl.nick), - htmlescape::encode_minimal(&new_nick))), + html: hformat!("{} changed {}'s nick to {}", + &who, gpl.nick, new_nick), }); gpl.nick = new_nick; } @@ -532,10 +530,10 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( } let new_links = Arc::new(new_links); *ig_links = new_links.clone(); - Ok(Html( - format!("{} set the links to off-server game resources", - &who.0) - )) + Ok( + hformat!("{} set the links to off-server game resources", + who) + ) })? } @@ -547,10 +545,10 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( new_links[kind] = Some(url.into_string()); let new_links = Arc::new(new_links); *ig_links = new_links.clone(); - Ok(Html( - format!("{} set the link {}", - &who.0, &show.0) - )) + Ok( + hformat!("{} set the link {}", + who, show) + ) })? } @@ -560,10 +558,10 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( new_links[kind] = None; let new_links = Arc::new(new_links); *ig_links = new_links.clone(); - Ok(Html( - format!("{} removed the link {}", - &who.0, &kind) - )) + Ok( + hformat!("{} removed the link {}", + who, &kind) + ) })? } @@ -599,8 +597,8 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( let (gpl, ipl, update) = got.into_iter().next() .ok_or(PlayerNotFound)?; - let html = Html( - format!("{} [{}] left the game [{}]" + let html = + hformat!("{} [{}] left the game [{}]" , (|| Some(gpl?.nick))() .unwrap_or("".into()) @@ -613,7 +611,7 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( , &account ) - ); + ; (U{ pcs: vec![], log: vec![ LogEntry { html }], @@ -654,7 +652,7 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( let pri = PieceRenderInstructions::new_visible(default()); pri.describe(ioccults,&gs.occults, gpc, &ipc) } else { - Html::lit("<piece partially missing from game state!>") + "".to_html() }; if let Some(gpc) = gpc { ipc.p.into_inner().delete_hook(&gpc, gs); @@ -662,8 +660,8 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( if let Some(occilk) = ipc.occilk { ig.ioccults.ilks.dispose(occilk); } (U{ pcs: vec![(piece, PieceUpdateOp::Delete())], log: vec![ LogEntry { - html: Html(format!("A piece {} was removed from the game", - desc_html.0)), + html: hformat!("A piece {} was removed from the game", + desc_html), }], raw: None }, Fine, @@ -741,7 +739,7 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( (U{ pcs: updates, log: vec![ LogEntry { - html: Html(format!("{} added {} pieces", &who.0, count_len)), + html: hformat!("{} added {} pieces", who, count_len), }], raw: None }, Fine, None, ig_g) @@ -762,7 +760,7 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( } (U{ pcs: vec![ ], log: vec![ LogEntry { - html: Html(format!("{} cleared the log", &who.0)), + html: hformat!("{} cleared the log", who), } ], raw: None }, Fine, None, ig) @@ -772,8 +770,8 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( let (ig, _) = cs.check_acl(&ag, ig, PCH::Instance, &[TP::Super])?; ig.acl = acl.into(); let mut log = vec![ LogEntry { - html: Html(format!("{} set the table access control list", - &who.0)), + html: hformat!("{} set the table access control list", + who), } ]; #[throws(InternalError)] @@ -813,7 +811,7 @@ fn execute_game_insn<'cs, 'igr, 'ig: 'igr>( "partial data?!".to_string() }; log.push(LogEntry { - html: Html(format!("{} removed a player {}", &who.0, &show)), + html: hformat!("{} removed a player {}", who, show), }); updates.push(update); } @@ -856,11 +854,11 @@ fn execute_for_game<'cs, 'igr, 'ig: 'igr>( .filter(|(_,ipr)| ipr.ipl.acctid == acctid) .next(); if let Some(gpl) = ig.gs.players.get(player); - then { Html(format!("{} [{}]", - &htmlescape::encode_minimal(&gpl.nick), - &account)) } - else { Html(format!("[{}]", - &account)) } + then { hformat!("{} [{}]", + gpl.nick, + account) } + else { hformat!("[{}]", + account) } }; let res = (||{ for insn in insns.drain(0..) { @@ -983,7 +981,7 @@ impl UpdateHandler { if bulk.logs { buf.log_updates(vec![LogEntry { - html: Html(format!("{} (re)configured the game", &who.0)), + html: hformat!("{} (re)configured the game", who), }]); } diff --git a/daemon/session.rs b/daemon/session.rs index a6c7b54a..29e1d315 100644 --- a/daemon/session.rs +++ b/daemon/session.rs @@ -174,10 +174,10 @@ fn session_inner(form: Json, l.into_iter() .map(|(trk,trv)|{ let when = trv.latest; - let html = Html(format!( + let html = hformat!( "player state accessed via {} [{}]", - &trk.desc.0, &trk.account - )); + trk.desc, trk.account + ); Arc::new(CommittedLogEntry { when, logent: LogEntry { html } }) }) }, diff --git a/src/accounts.rs b/src/accounts.rs index a5f21141..a55b2662 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -200,6 +200,7 @@ impl Display for AccountName { ], |s| f.write_str(s))? } } +hformat_as_display!{ AccountName } impl FromStr for AccountName { type Err = InvalidScopedName; diff --git a/src/bin/otterlib.rs b/src/bin/otterlib.rs index 3c2f674b..f04f25e1 100644 --- a/src/bin/otterlib.rs +++ b/src/bin/otterlib.rs @@ -127,13 +127,22 @@ fn preview(items: Vec) { println!(r#""#); for s in &pieces { let Prep { spec, p, uos, bbox, size } = s; + + macro_rules! println { + ($($x:tt)*) => ({ + let mut s = Html::new(); + hwrite!(&mut s, $($x)*).unwrap(); + std::println!("{}", &s.as_html_str()); + }) + } + println!(r#""#); println!(r#""#, - Html::from_txt(&spec.lib).0); + Html::from_txt(&spec.lib)); println!(r#""#, - Html::from_txt(&spec.item).0); + Html::from_txt(&spec.item)); println!(r#""#, - p.describe_html(&GPiece::dummy(), &default())?.0); + p.describe_html(&GPiece::dummy(), &default())?); let only1 = s.face_cols() == 1; for facecol in 0..(if only1 { 1 } else { max_facecols }) { @@ -169,22 +178,24 @@ fn preview(items: Vec) { &viewport, wh[0], wh[1]); if inseveral == 1 { let dasharray = player_num_dasharray(1.try_into().unwrap()); - print!(r#""#, - &surround.0, &dasharray.0, HELD_SURROUND_COLOUR); + &surround, &dasharray, HELD_SURROUND_COLOUR); } - let mut html = Html("".into()); + let mut html = Html::lit("").into(); let gpc = GPiece { face: face.into(), ..GPiece::dummy() }; p.svg_piece(&mut html, &gpc, &GameState::dummy(), default())?; - println!("{}", html.0); + println!("{}", html); } println!(""); if max_uos > 0 { println!(r#""#, uos.iter() - .map(|uo| Html::from_txt(uo).0) - .join(" ")); + .map(|uo| Html::from_txt(uo)) + .collect::>() + .iter() + .hjoin(&Html::lit(" "))); } }; println!(""); diff --git a/src/clock.rs b/src/clock.rs index 070889df..f918a26d 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -142,6 +142,7 @@ impl fmt::Debug for User { write!(f, "User({})", self) } } +hformat_as_display!{User} #[derive(Debug,Clone,Copy,Error,Serialize,Deserialize)] struct BadClockUserError; @@ -439,7 +440,6 @@ impl PieceTrait for Clock { #[throws(IE)] fn svg_piece(&self, f: &mut Html, gpc: &GPiece, gs: &GameState, vpid: VisiblePieceId) { - let f = &mut f.0; let state = gpc.xdata()? .ok_or_else(|| internal_logic_error("missing/wrong xdata"))?; let urenders = self.urender(&state, gpc.held, &gs.players); @@ -470,20 +470,20 @@ impl PieceTrait for Clock { } } - write!(f, r##" + hwrite!(f, r##" "##, )?; for (y, u) in izip!(Y.iter(), urenders.iter()) { - write!(f, r##" + hwrite!(f, r##" "##, - y, - u.st.show().background, + y, + Html::lit(u.st.show().background), )?; } - write!(f, r##" + hwrite!(f, r##" "##, - vpid + &vpid )?; for (user, y, u) in izip!(USERS.iter(), Y.iter(), urenders.iter()) { let y = y + 6.; @@ -491,49 +491,51 @@ impl PieceTrait for Clock { let mins = u.remaining.tv_sec() / 60; let secs = u.remaining.tv_sec() % 60; let mins = mins.to_string(); - let mins_pad = iter::repeat(" ").take(3 - mins.len()) - .collect::(); + let mins_pad = Html::from_html_string( + iter::repeat(" ").take(3 - mins.len()) + .collect() + ); - let pointer = r##" + let pointer = Html::lit(r##" pointer-events="none" - "##; - let font = r##" + "##); + let font = Html::lit(r##" font-family="Latin Modern Mono, monospace" font-size="6" font-weight="700" - "##; - write!(f, r##" + "##); + hwrite!(f, r##" {}{}{}"##, - y, font, pointer, show.text, - mins_pad, mins, show.sigil + y, font, pointer, Html::lit(show.text), + mins_pad, HtmlStr::from_html_str(&mins), Html::lit(show.sigil) )?; - write!(f, r##" + hwrite!(f, r##" {:02}"##, - y, font, pointer, show.text, + y, font, pointer, Html::lit(show.text), secs )?; let nick_y = y - 0.5; if let Some(nick) = u.nick { - write!(f, r##" + hwrite!(f, r##" {} "##, nick_y, pointer, vpid, - htmlescape::encode_minimal(nick), + nick, )?; } else { - write!(f, r##" + hwrite!(f, r##" ({})"##, nick_y, pointer, user, )?; } } - write!(f, r##" + hwrite!(f, r##" "##)?; } #[throws(IE)] fn describe_html(&self, _gpc: &GPiece, _goccults: &GameOccults) -> Html { - Html::lit("the chess clock") + Html::lit("the chess clock").into() } #[throws(InternalError)] @@ -542,8 +544,11 @@ impl PieceTrait for Clock { let state: &State = gpc.xdata_exp()?; let for_users = || izip!(&USERS, &USERINFOS, &state.users).map( - |(&user, userinfo, ust)| (user, userinfo, ust, - userinfo.idchar.to_ascii_uppercase()) + |(&user, userinfo, ust)| { + let upchar = userinfo.idchar.to_ascii_uppercase(); + let upchar = IsHtmlFormatted(upchar); + (user, userinfo, ust, upchar) + } ); for (user, userinfo, _ust, upchar) in for_users() { @@ -552,11 +557,11 @@ impl PieceTrait for Clock { kind: UoKind::Piece, def_key: userinfo.idchar, opname: format!("start-{}", userinfo.idchar), - desc: Html(if state.current.is_none() { - format!("Start, with player {}", &upchar) + desc: if state.current.is_none() { + hformat!("Start, with player {}", &upchar) } else { - format!("Make player {} current", &upchar) - }), + hformat!("Make player {} current", &upchar) + }, wrc: WRC::Predictable, }); } @@ -567,7 +572,7 @@ impl PieceTrait for Clock { kind: UoKind::Piece, def_key: 'S', opname: "stop".to_string(), - desc: Html::lit("Stop"), + desc: Html::lit("Stop").into(), wrc: WRC::Predictable, }); } @@ -576,7 +581,7 @@ impl PieceTrait for Clock { kind: UoKind::Piece, def_key: 'R', opname: "reset".to_string(), - desc: Html::lit("Reset"), + desc: Html::lit("Reset").into(), wrc: WRC::Unpredictable, }); } @@ -585,17 +590,17 @@ impl PieceTrait for Clock { if let Some(_gpl) = gs.players.get(ust.player) { upd.push(UoDescription { kind: UoKind::Piece, - def_key: upchar, + def_key: upchar.0, opname: format!("unclaim-{}", userinfo.idchar), - desc: Html(format!("Clear player {}", &upchar)), + desc: hformat!("Clear player {}", &upchar), wrc: WRC::Unpredictable, }); } else { upd.push(UoDescription { kind: UoKind::Piece, - def_key: upchar, + def_key: upchar.0, opname: format!("claim-{}", userinfo.idchar), - desc: Html(format!("Become player {}", &upchar)), + desc: hformat!("Become player {}", &upchar), wrc: WRC::Unpredictable, }); } @@ -683,7 +688,7 @@ impl PieceTrait for Clock { let log = log_did_to_piece(ioccults,&gs.occults, gpl, gpc, ipc, &did) .unwrap_or_else(|e| { error!("failed to log: {:?}", &e); - vec![LogEntry { html: Html::lit("<failed to log>") }] + vec![LogEntry { html: "".to_html() }] }); gpc.moveable = moveable; diff --git a/src/deck.rs b/src/deck.rs index 86c3d9df..e5e7e6f1 100644 --- a/src/deck.rs +++ b/src/deck.rs @@ -4,10 +4,10 @@ use crate::prelude::*; -pub const CORE_DESC: &str = "a play/pickup deck"; -pub const DISABLED_DESC: &str = "a play/pickup deck (disabled)"; -pub const COUNTING_DESC: &str = "a play pile (counting)"; -pub const ENABLED_DESC: &str = "a pickup deck (enabled)"; +pub const CORE_DESC : HtmlLit = Html::lit("a play/pickup deck"); +pub const DISABLED_DESC: HtmlLit = Html::lit("a play/pickup deck (disabled)"); +pub const COUNTING_DESC: HtmlLit = Html::lit("a play pile (counting)"); +pub const ENABLED_DESC : HtmlLit = Html::lit("a pickup deck (enabled)"); #[derive(Debug,Serialize,Deserialize)] struct Deck { @@ -102,20 +102,18 @@ impl PieceTrait for Deck { label.svg(f, &self.shape.outline, self.shape.edges.get(0), - &Html(count.to_string()))?; + &count.to_html())?; } } } #[throws(IE)] fn describe_html(&self, gpc: &GPiece, goccults: &GameOccults) -> Html { - Html::lit( - match self.state(gpc, goccults)? { - Disabled => DISABLED_DESC, - Counting => ENABLED_DESC, - Enabled => ENABLED_DESC, - } - ) + match self.state(gpc, goccults)? { + Disabled => DISABLED_DESC, + Counting => ENABLED_DESC, + Enabled => ENABLED_DESC, + }.into() } delegate!{ @@ -133,7 +131,7 @@ impl PieceTrait for Deck { kind: UoKind::Piece, def_key: 'A', opname: "activate".to_owned(), - desc: Html::lit("Enable pickup deck"), + desc: Html::lit("Enable pickup deck").into(), wrc: WRC::Unpredictable, }); } @@ -142,7 +140,7 @@ impl PieceTrait for Deck { kind: UoKind::Piece, def_key: 'C', opname: "counting".to_owned(), - desc: Html::lit("Make into counting play pile"), + desc: Html::lit("Make into counting play pile").into(), wrc: WRC::Unpredictable, }); } @@ -151,7 +149,7 @@ impl PieceTrait for Deck { kind: UoKind::Piece, def_key: 'S', opname: "deactivate".to_owned(), - desc: Html::lit("Disable pickup deck"), + desc: Html::lit("Disable pickup deck").into(), wrc: WRC::Unpredictable, }); } @@ -170,7 +168,7 @@ impl PieceTrait for Deck { let gpc = gpieces.byid_mut(piece)?; let gpl = gplayers.byid_mut(player)?; - let nick = Html(htmlescape::encode_minimal(&gpl.nick)); + let nick = gpl.nick.to_html(); dbgc!("ui op k entry", &opname); @@ -180,9 +178,9 @@ impl PieceTrait for Deck { let old_state = self.state(gpc, &goccults).map_err(err_via_response)?; let (new_state, did) = match opname { - "activate" => (Enabled, format!("enabled {}", CORE_DESC)), - "counting" => (Counting, format!("set {} to counting", CORE_DESC)), - "deactivate" => (Disabled, format!("disabled {}", CORE_DESC)), + "activate" => (Enabled, hformat!("enabled {}", CORE_DESC)), + "counting" => (Counting, hformat!("set {} to counting", CORE_DESC)), + "deactivate" => (Disabled, hformat!("disabled {}", CORE_DESC)), _ => throw!(OE::BadOperation), }; @@ -228,7 +226,7 @@ impl PieceTrait for Deck { dbgc!("creating occ done", &xupdates); } - let log = vec![ LogEntry { html: Html(format!("{} {}", nick.0, did)) }]; + let log = vec![ LogEntry { html: hformat!("{} {}", nick, did) }]; dbgc!("ui op k did main"); diff --git a/src/gamestate.rs b/src/gamestate.rs index d6a9cb26..a1c73509 100644 --- a/src/gamestate.rs +++ b/src/gamestate.rs @@ -19,10 +19,6 @@ pub struct Generation(pub u64); visible_slotmap_key!{ VisiblePieceId(b'.') } -#[derive(Clone,Serialize,Deserialize,Hash,Eq,Ord,PartialEq,PartialOrd)] -#[serde(transparent)] -pub struct Html(pub String); - #[derive(Copy,Clone,Debug,Serialize,Deserialize,Eq,Ord,PartialEq,PartialOrd)] #[serde(transparent)] pub struct Timestamp(pub u64); /* time_t */ @@ -113,6 +109,7 @@ mod item_name { #[throws(fmt::Error)] fn fmt(&self, f: &mut fmt::Formatter) { f.write_str(&self.0)? } } + hformat_as_display!{ GoodItemName } impl TryFrom for GoodItemName { type Error = LibraryLoadError; @@ -327,25 +324,6 @@ impl ClampTable for Pos { } } -impl Html { - // todo convert to display_as but I need to write display_as::typed::Is - pub fn lit(s: &str) -> Self { Html(s.to_owned()) } - pub fn from_txt(s: &str) -> Self { - Html(htmlescape::encode_minimal(&s)) - } -} - -impl Debug for Html { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - const MAX: usize = 23; - if self.0.len() < MAX { - write!(f, "<{}>", &self.0) - } else { - write!(f, "<{}>...", &self.0[0..MAX-3]) - } - } -} - // ---------- game state - rendering etc. ---------- impl GPiece { @@ -457,7 +435,7 @@ fn xdata_get_mut_inner< impl GameState { pub fn dummy() -> Self { GameState { - table_colour: Html::lit("green"), + table_colour: Html::lit("green").into(), table_size: PosC::new(300,200), pieces: default(), gen: Generation(0), @@ -488,7 +466,7 @@ impl GameState { let front = self.log.front_mut().unwrap(); let front = &mut front.1; let logent = LogEntry { - html: Html::lit("[Earlier log entries expired]"), + html: Html::lit("[Earlier log entries expired]").into(), }; *front = Arc::new(CommittedLogEntry { logent, when: front.when }); } diff --git a/src/global.rs b/src/global.rs index 488ca336..98652049 100644 --- a/src/global.rs +++ b/src/global.rs @@ -471,8 +471,10 @@ impl Instance { }) }).collect::>(); let render = RenderContext { players }; - let html = Html(nwtemplates::render("player-info-pane.tera", &render) - .context("render player info template")?); + let html = Html::from_html_string( + nwtemplates::render("player-info-pane.tera", &render) + .context("render player info template")? + ); html } } @@ -518,13 +520,12 @@ impl Display for InstanceName { } } -fn link_a_href(k: &K, v: &str) -> Html { - let url = htmlescape::encode_minimal(v); - Html(format!("{kind}", url=url, kind=k)) +fn link_a_href(k: &HtmlStr, v: &str) -> Html { + hformat!("{}", v, k) } impl From<(LinkKind, &str)> for Html { fn from((k, v): (LinkKind, &str)) -> Html { - link_a_href(&k, v) + link_a_href(&k.to_html(), v) } } @@ -533,14 +534,16 @@ impl From<&LinksTable> for Html { let mut s = links.iter() .filter_map(|(k,v)| { let v = v.as_ref()?; - Some(Html::from((k, v.as_str())).0) + Some(Html::from((k, v.as_str()))) }) .chain(iter::once( - link_a_href(&"Shapelib", "/_/shapelib.html").0 + link_a_href(Html::lit("Shapelib").into(), "/_/shapelib.html") )) - .join(" "); - if s.len() != 0 { s = format!("links: {}", s) } - Html(s) + .collect::>() + .iter() + .hjoin(&Html::lit(" ")); + if s.len() != 0 { s = hformat!("links: {}", s) } + s } } diff --git a/src/hand.rs b/src/hand.rs index e35be637..3bdf39ba 100644 --- a/src/hand.rs +++ b/src/hand.rs @@ -4,7 +4,7 @@ use crate::prelude::*; -pub const UNCLAIMED_DESC: &str = "a hand repository"; +pub const UNCLAIMED_DESC: HtmlLit = Html::lit("a hand repository"); #[derive(Debug,Clone,Serialize,Deserialize)] struct MagicOwner { @@ -81,7 +81,7 @@ impl Hand { if let Some(xdata) = xdata; if let Some(owner) = &xdata.owner; then { owner.desc.clone() } - else { Html(UNCLAIMED_DESC.into()) } + else { UNCLAIMED_DESC.into() } } } } @@ -98,9 +98,9 @@ impl PieceTrait for Hand { then { Some(owned) } else { None } }; - self.shape.svg_piece_raw(f, gpc.face, &mut |f: &mut String| { + self.shape.svg_piece_raw(f, gpc.face, &mut |f: &mut Html| { if let Some(owned) = owned { - write!(f, r##" stroke-dasharray="{}" "##, &owned.dasharray.0)?; + hwrite!(f, r##" stroke-dasharray="{}" "##, &owned.dasharray)?; } Ok(()) })?; @@ -112,7 +112,7 @@ impl PieceTrait for Hand { label.svg(f, &self.shape.outline, self.shape.edges.get(0), - &Html(htmlescape::encode_minimal(&gpl.nick)))?; + &gpl.nick.to_html())? } } } @@ -139,14 +139,14 @@ impl PieceTrait for Hand { kind: UoKind::Piece, def_key: 'C', opname: "deactivate".to_owned(), - desc: Html::lit("Deactivate hand"), + desc: Html::lit("Deactivate hand").into(), wrc: WRC::Unpredictable, }} else { UoDescription { kind: UoKind::Piece, def_key: 'C', opname: "claim".to_owned(), - desc: Html::lit("Claim this as your hand"), + desc: Html::lit("Claim this as your hand").into(), wrc: WRC::Unpredictable, }} }) @@ -170,7 +170,7 @@ impl PieceTrait for Hand { let dasharray = player_dasharray(gplayers, player); let gpl = gplayers.byid_mut(player)?; - let nick = Html(htmlescape::encode_minimal(&gpl.nick)); + let nick = gpl.nick.to_html(); dbgc!("ui op k entry", &opname, &xdata); @@ -179,7 +179,7 @@ impl PieceTrait for Hand { { ("claim", false) => { dbgc!("claiming"); - let new_desc = Html(format!("{}'s hand", &nick.0)); + let new_desc = hformat!("{}'s hand", &nick); let new_owner = Some(MagicOwner { player, dasharray, @@ -208,7 +208,7 @@ impl PieceTrait for Hand { region, piece, views)?; dbgc!("creating occ done", &new_owner, &xupdates); - (new_owner, xupdates, format!("claimed {}", &old_desc.0)) + (new_owner, xupdates, hformat!("claimed {}", &old_desc)) } ("deactivate", true) => { let xupdates = @@ -217,7 +217,7 @@ impl PieceTrait for Hand { to_recalculate, piece) .map_err(|ie| ApiPieceOpError::ReportViaResponse(ie.into()))?; - (None, xupdates, format!("deactivated {}", &old_desc.0)) + (None, xupdates, hformat!("deactivated {}", &old_desc)) } ("claim", true) | ("deactivate", false) => { @@ -228,7 +228,7 @@ impl PieceTrait for Hand { } }; - let log = vec![ LogEntry { html: Html(format!("{} {}", nick.0, did)) }]; + let log = vec![ LogEntry { html: hformat!("{} {}", nick, did) }]; dbgc!("ui op k did main"); diff --git a/src/hidden.rs b/src/hidden.rs index 23372f33..7f262023 100644 --- a/src/hidden.rs +++ b/src/hidden.rs @@ -686,17 +686,17 @@ pub fn recalculate_occultation_piece( to_recalculate, piece, || (vanilla_wrc, vanilla_op, vanilla_log).into(), vec![], - |old, new, Html(show)| vec![ LogEntry { html: Html(format!( + |old, new, show| vec![ LogEntry { html: hformat!( "{} {}", - &who_by.0, + &who_by, match (old, new) { - (None, None) => format!("modified {} somehow", show), - (Some(old), None) => format!("produced {} from {}", show, &old.0), - (None, Some(new)) => format!("placed {} into {}", show, &new.0), - (Some(old), Some(new)) => format!("moved {} from {} to {}", - show, &old.0, &new.0), - }, - ))}], + (None, None) => hformat!("modified {} somehow", show), + (Some(old), None) => hformat!("produced {} from {}", show, old), + (None, Some(new)) => hformat!("placed {} into {}", show, new), + (Some(old), Some(new)) => hformat!("moved {} from {} to {}", + show, old, new), + } + )}], |puos, log| PieceUpdate { wrc: WRC::Unpredictable, ops: PieceUpdateOps::PerPlayer(puos), diff --git a/src/keydata.rs b/src/keydata.rs index 16569b66..7fd360cd 100644 --- a/src/keydata.rs +++ b/src/keydata.rs @@ -53,6 +53,7 @@ macro_rules! visible_slotmap_key { pub struct $x(pub slotmap::KeyData); impl_for_slotmap_key!($x($sep)); + hformat_as_display!{$x} } } #[macro_export] diff --git a/src/lib.rs b/src/lib.rs index 5ddbdc10..a15a6b3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub mod debugreader; pub mod error; pub mod gamestate; pub mod global; +pub mod html; pub mod hand; pub mod hidden; pub mod keydata; diff --git a/src/pcrender.rs b/src/pcrender.rs index ce196c2c..f5c889a0 100644 --- a/src/pcrender.rs +++ b/src/pcrender.rs @@ -9,7 +9,7 @@ pub type VisiblePieceAngle = PieceAngle; #[derive(Clone,Debug)] #[derive(Serialize,Deserialize)] #[serde(transparent)] -pub struct VisibleAngleTransform(String); +pub struct VisibleAngleTransform(Html); #[derive(Debug,Clone)] pub struct PieceRenderInstructions { @@ -29,9 +29,10 @@ pub type PriOcculted = PriOccultedGeneral; impl VisiblePieceAngle { pub fn to_transform(self) -> VisibleAngleTransform { - VisibleAngleTransform(base_misc::raw_angle_transform( - self.to_compass().into() - )) + VisibleAngleTransform(Html::from_html_string( + base_misc::raw_angle_transform( + self.to_compass().into() + ))) } pub fn to_compass(self) -> CompassAngle { @@ -63,7 +64,7 @@ impl PriOccultedGeneral { self.describe_fallible(ioccults, goccults, gpc, ipc) .unwrap_or_else(|e| { error!("error describing piece: {:?}", e); - Html::lit("<internal error describing piece>") + "".to_html() }) } @@ -184,8 +185,8 @@ impl PieceRenderInstructions { let transform = angle.to_transform(); - let mut defs = Html(String::new()); - write!(&mut defs.0, + let mut defs = Html::new(); + hwrite!(&mut defs, r##""##, pri.vpid, &transform.0, dragraise)?; @@ -198,10 +199,10 @@ impl PieceRenderInstructions { }, }; - write!(&mut defs.0, r##""##)?; - write!(&mut defs.0, + hwrite!(&mut defs, r##""##)?; + hwrite!(&mut defs, r##""##, - pri.vpid, o.surround_path()?.0)?; + pri.vpid, o.surround_path()?)?; defs } @@ -223,7 +224,7 @@ impl PieceRenderInstructions { kind: UoKind::Global, def_key: 'f'.into(), opname: "flip".to_string(), - desc: Html::lit("flip"), + desc: Html::lit("flip").into(), }) } ipc.show(y).add_ui_operations(&mut out, gs, gpc)?; diff --git a/src/pieces.rs b/src/pieces.rs index b1546741..c87f3666 100644 --- a/src/pieces.rs +++ b/src/pieces.rs @@ -46,7 +46,7 @@ impl From for MgmtError { } #[throws(SvgE)] -pub fn svg_rescale_path(input: &Html, scale: f64) -> Html { +pub fn svg_rescale_path(input: &HtmlStr, scale: f64) -> Html { type BM = u64; type BI = u32; #[derive(Debug,Copy,Clone)] @@ -71,7 +71,7 @@ pub fn svg_rescale_path(input: &Html, scale: f64) -> Html { let mut map = ALWAYS_MAP; let mut first = iter::once(()); - for w in input.0.split_ascii_whitespace() { + for w in input.as_html_str().split_ascii_whitespace() { if first.next().is_none() { write!(&mut out, " ")?; } match w { "L" | "l" | "M" | "m" | @@ -91,7 +91,7 @@ pub fn svg_rescale_path(input: &Html, scale: f64) -> Html { } trace!("rescaled by {}: {:?} as {:?}", scale, input, &out); - Html(out) + Html::from_html_string(out) } #[throws(SvgE)] @@ -101,14 +101,14 @@ pub fn svg_circle_path(diam: f64) -> Html { a 1 1 0 1 0 0 2 z" ); let scale = diam * 0.5; - let path = svg_rescale_path(&unit_path, scale)?; + let path = svg_rescale_path(unit_path.into(), scale)?; path } #[throws(SvgE)] pub fn svg_rectangle_path(PosC{coords: [x,y]}: PosC) -> Html { - Html(format!("M {} {} h {} v {} h {} z", - -x*0.5, -y*0.5, x, y, -x)) + hformat!("M {} {} h {} v {} h {} z", + -x*0.5, -y*0.5, x, y, -x) } #[dyn_upcast] @@ -133,14 +133,19 @@ impl PieceTrait for SimpleShape { _gs: &GameState, _vpid: VisiblePieceId) { self.svg_piece_raw(f, gpc.face, &mut |_|Ok(()))?; } - #[throws(IE)] - fn describe_html(&self, gpc: &GPiece, _goccults: &GameOccults) -> Html { - Html(if_chain! { +// #[throws(IE)] + fn describe_html(&self, gpc: &GPiece, _goccults: &GameOccults) +// -> Html +-> Result + { +Ok::<_,IE>( + if_chain! { if let face = gpc.face; if let Some(colour) = self.colours.get(face); - then { format!("a {} {}", colour.0, self.desc.0) } - else { format!("a {}", self.desc.0) } - }) + then { hformat!("a {} {}", colour, self.desc) } + else { hformat!("a {}", self.desc) } + } +) } fn nfaces(&self) -> RawFaceId { self.count_faces().try_into().unwrap() @@ -179,9 +184,9 @@ impl PieceLabelLoaded { def_colour: Option<&Colour>, text: &Html) { let colour = { - if let Some(c) = &self.colour { &c.0 } - else if let Some(c) = def_colour { &c.0 } - else { "black" } + if let Some(c) = &self.colour { c.borrow() } + else if let Some(c) = def_colour { c.borrow() } + else { Html::lit("black").into() } }; let fontsz = 4.; let PosC{ coords: [x,y] } = { @@ -201,9 +206,9 @@ impl PieceLabelLoaded { *y += 0.5 * fontsz; pos }; - write!(f.0, - r##"{}"##, - x, y, fontsz, colour, &text.0 + hwrite!(f, + r##"{}"##, + x, y, fontsz, colour, &text )?; } } @@ -261,33 +266,32 @@ impl GenericSimpleShape #[throws(IE)] pub fn svg_piece_raw( &self, f: &mut Html, face: FaceId, - stroke_attrs_hook: &mut dyn FnMut(&mut String) -> Result<(),IE>, + stroke_attrs_hook: &mut dyn FnMut(&mut Html) -> Result<(),IE>, ) { - let f = &mut f.0; - let ef = |f: &mut String, cmap: &ColourMap, attrname: &str, otherwise| { + let ef = |f: &mut Html, cmap: &ColourMap, attrname: &str, otherwise| { if let Some(colour) = cmap.get(face) { - write!(f, r##" {}="{}""##, attrname, colour.0) + hwrite!(f, r##" {}="{}""##, attrname, colour) } else { - write!(f, "{}", otherwise) + hwrite!(f, "{}", otherwise) } }; let path = self.outline_path(1.0)?; if self.colours.len() == 0 { - write!(f, + hwrite!(f, r##""##, INVISIBLE_EDGE_SENSITIVE, - &path.0)?; + &path)?; } - write!(f, r##""##, &path.0)?; + hwrite!(f, r##" d="{}"/>"##, &path)?; } } @@ -306,7 +310,7 @@ impl SimplePieceSpec for piece_specs::Disc { fn load_raw(&self) -> (SimpleShape, &SimpleCommon) { let outline = CircleShape { diam: self.diam as f64 }; (SimpleShape::new( - Html::lit("disc"), + Html::lit("disc").into(), outline.into(), "simple-disc", &self.common, @@ -343,7 +347,7 @@ impl SimplePieceSpec for piece_specs::Rect { let desc = Html::lit( if outline.xy.x() == outline.xy.y() { "square" } else { "rectangle" } - ); + ).into(); (SimpleShape::new( desc, outline.into(), diff --git a/src/prelude.rs b/src/prelude.rs index 8d98bb88..cc097b5d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -115,6 +115,7 @@ pub use crate::dbgc; pub use crate::{deref_to_field, deref_to_field_mut}; pub use crate::ensure_eq; pub use crate::from_instance_lock_error; +pub use crate::{hformat, hformat_as_display, hwrite}; pub use crate::matches_doesnot; pub use crate::trace_dbg; @@ -133,6 +134,7 @@ pub use crate::fake_rng::*; pub use crate::gamestate::*; pub use crate::global::*; pub use crate::hidden::*; +pub use crate::html::*; pub use crate::keydata::*; pub use crate::mgmtchannel::*; pub use crate::nwtemplates; diff --git a/src/shapelib-toml.rs b/src/shapelib-toml.rs index de5bf79b..4b03ba9d 100644 --- a/src/shapelib-toml.rs +++ b/src/shapelib-toml.rs @@ -215,8 +215,9 @@ pub struct FileData { #[cfg(doc)] pub r_file_spec: String, #[cfg(not(doc))] pub r_file_spec: (), - /// HTML desscription. (Shown hn the game log, for example.) - pub desc: Html, + /// Desscription. (Shown hn the game log, for example.) + /// Will be HTML-escaped. + pub desc: String, } #[cfg(doc)] diff --git a/src/shapelib.rs b/src/shapelib.rs index 24a26d2d..4514e3bd 100644 --- a/src/shapelib.rs +++ b/src/shapelib.rs @@ -200,7 +200,7 @@ pub struct ItemEnquiryData { impl ItemEnquiryData { pub fn line_for_list(&self) -> String { - format!("{:20} {}", self.itemname.as_str(), self.f0desc.0) + format!("{:20} {}", self.itemname, self.f0desc.as_html_str()) } } @@ -249,10 +249,10 @@ impl FaceTransform { #[throws(IE)] fn write_svgd(&self, f: &mut Html, svgd: &Html) { - write!(&mut f.0, + hwrite!(f, r##"{}"##, self.scale[0], self.scale[1], -self.centre[0], -self.centre[1], - svgd.0)?; + svgd)?; } } @@ -366,7 +366,7 @@ impl Contents { SpE::InternalError(m.to_string()) })?; - Html(svg_data) + Html::from_html_string(svg_data) } #[throws(SpecError)] @@ -645,14 +645,14 @@ fn load_catalogue(libname: &str, dirname: &str, toml_path: &str) -> Contents { if ! group.d.colours.contains_key(colour.0.as_str()) { throw!(LLE::OccultationColourMissing(colour.0.clone())); } - let colour: Colour = colour.try_into()?; let item_name = subst(item_name.as_str(), "_c", &colour.0)?; + let desc = subst(&fe.desc, "_colour", "")?.to_html(); OccData::Internal(Arc::new(OccData_Internal { item_name: Arc::new(item_name.try_into()?), - desc: Html(subst(&fe.desc.0, "_colour", "")?), outline: outline.clone(), xform: FaceTransform::from_group(&group.d)?, svgd: default(), + desc, })) }, Some(OccultationMethod::ByBack { ilk }) => { @@ -663,11 +663,11 @@ fn load_catalogue(libname: &str, dirname: &str, toml_path: &str) -> Contents { }, }; - let mut add1 = |item_name: &GoodItemName, desc: Html| { + let mut add1 = |item_name: &GoodItemName, desc: &str| { let desc = if let Some(desc_template) = &group.d.desc_template { - Html(subst(desc_template, "_desc", &desc.0)?) + subst(desc_template, "_desc", &desc)?.to_html() } else { - desc + desc.to_html() }; let idata = ItemData { group: group.clone(), @@ -691,12 +691,12 @@ fn load_catalogue(libname: &str, dirname: &str, toml_path: &str) -> Contents { }; if group.d.colours.is_empty() { - add1(&item_name, fe.desc.clone())?; + add1(&item_name, &fe.desc.clone())?; } else { for (colour, recolourdata) in &group.d.colours { let t_item_name = subst(item_name.as_str(), "_c", &recolourdata.abbrev)?; - let t_desc = Html(subst(&fe.desc.0, "_colour", colour)?); - add1(&t_item_name.try_into()?, t_desc)?; + let t_desc = subst(&fe.desc, "_colour", colour)?; + add1(&t_item_name.try_into()?, &t_desc)?; } } @@ -927,8 +927,8 @@ impl TryFrom for FileList { }; let item_spec = n()?; let _r_file_spec = n()?; - let desc = Html(remain.to_owned()); - o.push(FileData{ item_spec, r_file_spec: (), desc }); + let desc = remain.to_owned(); + o.push(FileData{ item_spec, r_file_spec: (), desc }); } Ok(FileList(o)) } diff --git a/src/spec.rs b/src/spec.rs index 5e5d5dbd..164193c8 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -26,6 +26,7 @@ use otter_base::misc::display_as_debug; use crate::accounts::AccountName; use crate::error::UnsupportedColourSpec; use crate::gamestate::PieceSpec; +use crate::hformat_as_display; use crate::prelude::default; pub use implementation::PlayerAccessSpec; @@ -140,6 +141,7 @@ pub enum LinkKind { Voice, Info, } +hformat_as_display!{LinkKind} //---------- player accesses, should perhaps be in commands.rs ---------- @@ -382,7 +384,7 @@ pub mod implementation { -> Result; fn describe_html(&self) -> Html { let inner = Html::from_txt(&format!("{:?}", self)); - Html(format!("{}", inner.0)) + hformat!("{}", inner) } } @@ -550,7 +552,7 @@ pub mod implementation { if !RE.is_match(s) { throw!(UnsupportedColourSpec); } - Html(spec.0.clone()) + Html::from_html_string(spec.0.clone()) } } diff --git a/src/ui.rs b/src/ui.rs index 94cf81f0..9ae5e6a6 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -26,7 +26,7 @@ pub fn player_num_dasharray(player_num: NonZeroUsize) -> Html { } let spc = dasharray.pop(); assert_eq!(spc,Some(' ')); - Html(dasharray) + Html::from_html_string(dasharray) } pub fn player_dasharray(gplayers: &GPlayers, player: PlayerId) -> Html { diff --git a/src/updates.rs b/src/updates.rs index bb1cec35..61e9509a 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -147,7 +147,7 @@ impl SecondarySlotMap { } impl JsonLen for Html { - fn json_len(&self) -> usize { self.0.as_bytes().len() } + fn json_len(&self) -> usize { self.as_html_str().as_bytes().len() } } // ---------- piece updates ---------- @@ -274,7 +274,7 @@ pub fn log_did_to_piece_whoby(ioccults: &IOccults, goccults: &GameOccults, gpc: &GPiece, ipc: &IPiece, did: &str) -> (Vec, Option) { - let who_by = Html(htmlescape::encode_minimal(&by_gpl.nick)); + let who_by = by_gpl.nick.to_html(); let y = gpc.fully_visible_to_everyone(); let desc = (||{ Ok::<_,IE>(match ipc.show_or_instead(ioccults, y)? { @@ -283,15 +283,15 @@ pub fn log_did_to_piece_whoby(ioccults: &IOccults, goccults: &GameOccults, }) })().unwrap_or_else(|e|{ error!("failed to format during logging: {:?}", e); - Html::lit("<internal error>") + Html::lit("<internal error>").into() }); - let log = vec![ LogEntry { html: Html(format!( + let log = vec![ LogEntry { html: hformat!( "{} {} {}", - &who_by.0, + who_by, did, - desc.0, - ))}]; + desc, + )}]; (log, Some(who_by)) } @@ -406,22 +406,22 @@ impl PreparedUpdateEntry { ims.json_len(player) } Log(logent) => { - logent.logent.html.0.as_bytes().len() * 28 + logent.logent.html.json_len() * 28 } AddPlayer { player:_, data: DataLoadPlayer { dasharray, nick, }, new_info_pane, } => { - dasharray.0.as_bytes().len() + - nick .0.as_bytes().len() + 100 - + new_info_pane.0.len() + dasharray.json_len() + + nick .json_len() + 100 + + new_info_pane.json_len() } RemovePlayer { player:_, new_info_pane } => { - new_info_pane.0.len() + 100 + new_info_pane.json_len() + 100 } SetTableColour(colour) => { - colour.0.as_bytes().len() + 50 + colour.json_len() + 50 } SetLinks(links) => { links.iter().filter_map( @@ -446,8 +446,8 @@ impl DataLoadPlayer { let gplayers = &ig.gs.players; let dasharray = player_dasharray(gplayers, player); let nick = gplayers.get(player).map(|gpl| gpl.nick.as_str()) - .unwrap_or(""); - let nick = Html(htmlescape::encode_minimal(&nick)); + .unwrap_or("") + .to_html(); DataLoadPlayer { dasharray, nick, diff --git a/wdriver/wdt-hand.rs b/wdriver/wdt-hand.rs index 1abb761f..303176db 100644 --- a/wdriver/wdt-hand.rs +++ b/wdriver/wdt-hand.rs @@ -23,8 +23,8 @@ pub fn player_dasharray(player: &'static str) -> String { let (player,_) = player.get_idx_version(); let player: usize = player.try_into().unwrap(); let player = player.try_into().unwrap(); - let dasharray = player_num_dasharray(player).0; - dasharray + let dasharray = player_num_dasharray(player); + dasharray.into_html_string() } impl Ctx { -- 2.30.2
{}{}{}{}