Переглянути джерело

Refactor nil checks, remove getAttr

Zed 7 роки тому
батько
коміт
e2039ec81c
5 змінених файлів з 48 додано та 52 видалено
  1. 1 0
      public/style.css
  2. 6 9
      src/api.nim
  3. 1 1
      src/nitter.nim
  4. 18 18
      src/parser.nim
  5. 22 24
      src/parserutils.nim

+ 1 - 0
public/style.css

@@ -498,6 +498,7 @@ nav {
 
 .thread-last .status-el::before {
     background: unset;
+    min-width: unset;
     width: 0;
     margin: 0;
 }

+ 6 - 9
src/api.nim

@@ -96,7 +96,7 @@ proc getVideo*(tweet: Tweet; token: string) {.async.} =
     url = apiBase / (videoUrl % tweet.id)
     json = await fetchJson(url, headers)
 
-  if json.isNil:
+  if json == nil:
     if getTime() - tokenUpdated > initDuration(seconds=1):
       tokenUpdated = getTime()
       guestToken = await getGuestToken(force=true)
@@ -134,8 +134,7 @@ proc getProfileFallback(username: string; headers: HttpHeaders): Future[Profile]
     url = base / profileIntentUrl ? {"screen_name": username}
     html = await fetchHtml(url, headers)
 
-  if html.isNil:
-    return Profile()
+  if html == nil: return Profile()
 
   result = parseIntentProfile(html)
 
@@ -158,10 +157,9 @@ proc getProfile*(username: string): Future[Profile] {.async.} =
     url = base / profilePopupUrl ? params
     html = await fetchHtml(url, headers, jsonKey="html")
 
-  if html.isNil:
-    return Profile()
+  if html == nil: return Profile()
 
-  if not html.select(".ProfileCard-sensitiveWarningContainer").isNil:
+  if html.select(".ProfileCard-sensitiveWarningContainer") != nil:
     return await getProfileFallback(username, headers)
 
   result = parsePopupProfile(html)
@@ -182,7 +180,7 @@ proc getTimeline*(username: string; after=""): Future[Timeline] {.async.} =
     url &= "&max_position=" & cleanAfter
 
   let json = await fetchJson(base / url, headers)
-  if json.isNil: return Timeline()
+  if json == nil: return Timeline()
 
   result = Timeline(
     hasMore: json["has_more_items"].to(bool),
@@ -213,8 +211,7 @@ proc getTweet*(username: string; id: string): Future[Conversation] {.async.} =
     url = base / username / tweetUrl / id
     html = await fetchHtml(url, headers)
 
-  if html.isNil:
-    return
+  if html == nil: return
 
   result = parseConversation(html)
   await getConversationVideos(result)

+ 1 - 1
src/nitter.nim

@@ -44,7 +44,7 @@ routes:
     cond '.' notin @"name"
 
     let conversation = await getTweet(@"name", @"id")
-    if conversation.isNil or conversation.tweet.id.len == 0:
+    if conversation == nil or conversation.tweet.id.len == 0:
       resp Http404, showError("Tweet not found")
 
     let title = pageTitle(conversation.tweet.profile)

+ 18 - 18
src/parser.nim

@@ -4,7 +4,7 @@ import ./types, ./parserutils, ./formatters
 
 proc parsePopupProfile*(node: XmlNode): Profile =
   let profile = node.select(".profile-card")
-  if profile.isNil: return
+  if profile == nil: return
 
   result = Profile(
     fullname:  profile.getName(".fullname"),
@@ -24,8 +24,8 @@ proc parseIntentProfile*(profile: XmlNode): Profile =
     username:  profile.getUsername(".nickname"),
     bio:       profile.getBio("p.note"),
     userpic:   profile.select(".profile.summary").getAvatar("img.photo"),
-    verified:  not profile.select("li.verified").isNil,
-    protected: not profile.select("li.protected").isNil,
+    verified:  profile.select("li.verified") != nil,
+    protected: profile.select("li.protected") != nil,
     banner:    getBanner(profile)
   )
 
@@ -33,22 +33,22 @@ proc parseIntentProfile*(profile: XmlNode): Profile =
 
 proc parseTweetProfile*(profile: XmlNode): Profile =
   result = Profile(
-    fullname: profile.getAttr("data-name").stripText(),
-    username: profile.getAttr("data-screen-name"),
+    fullname: profile.attr("data-name").stripText(),
+    username: profile.attr("data-screen-name"),
     userpic:  profile.getAvatar(".avatar"),
     verified: isVerified(profile)
   )
 
 proc parseQuote*(quote: XmlNode): Quote =
   result = Quote(
-    id:   quote.getAttr("data-item-id"),
-    link: quote.getAttr("href"),
+    id:   quote.attr("data-item-id"),
+    link: quote.attr("href"),
     text: getQuoteText(quote)
   )
 
   result.profile = Profile(
     fullname: quote.selectText(".QuoteTweet-fullname").stripText(),
-    username: quote.getAttr("data-screen-name"),
+    username: quote.attr("data-screen-name"),
     verified: isVerified(quote)
   )
 
@@ -56,17 +56,17 @@ proc parseQuote*(quote: XmlNode): Quote =
 
 proc parseTweet*(node: XmlNode): Tweet =
   let tweet = node.select(".tweet")
-  if tweet.isNil():
-    return Tweet()
+  if tweet == nil: return Tweet()
 
   result = Tweet(
-    id:        tweet.getAttr("data-item-id"),
-    link:      tweet.getAttr("data-permalink-path"),
-    profile:   parseTweetProfile(tweet),
+    id:        tweet.attr("data-item-id"),
+    link:      tweet.attr("data-permalink-path"),
     text:      getTweetText(tweet),
     time:      getTimestamp(tweet),
     shortTime: getShortTime(tweet),
-    pinned:    "pinned" in tweet.getAttr("class")
+    profile:   parseTweetProfile(tweet),
+    pinned:    "pinned" in tweet.attr("class"),
+    available: true
   )
 
   result.getTweetStats(tweet)
@@ -75,14 +75,14 @@ proc parseTweet*(node: XmlNode): Tweet =
   let by = tweet.selectText(".js-retweet-text > a > b")
   if by.len > 0:
     result.retweetBy = some(by.stripText())
-    result.retweetId = some(tweet.getAttr("data-retweet-id"))
+    result.retweetId = some(tweet.attr("data-retweet-id"))
 
   let quote = tweet.select(".QuoteTweet-innerContainer")
-  if not quote.isNil:
+  if quote != nil:
     result.quote = some(parseQuote(quote))
 
 proc parseTweets*(node: XmlNode): Tweets =
-  if node.isNil or node.kind == xnText: return
+  if node == nil or node.kind == xnText: return
   node.selectAll(".stream-item").map(parseTweet)
 
 proc parseConversation*(node: XmlNode): Conversation =
@@ -92,7 +92,7 @@ proc parseConversation*(node: XmlNode): Conversation =
   )
 
   let replies = node.select(".replies-to", ".stream-items")
-  if replies.isNil: return
+  if replies == nil: return
 
   for reply in replies.filterIt(it.kind != xnText):
     if "selfThread" in reply.attr("class"):

+ 22 - 24
src/parserutils.nim

@@ -10,24 +10,22 @@ const
   gifRegex = re".+thumb/([^\.']+)\.jpg.*"
 
 proc selectAll*(node: XmlNode; selector: string): seq[XmlNode] =
+  if node == nil: return
   q.select(node, selector)
 
 proc select*(node: XmlNode; selector: string): XmlNode =
+  if node == nil: return
   let nodes = node.selectAll(selector)
   if nodes.len > 0: nodes[0] else: nil
 
 proc select*(node: XmlNode; parent, child: string): XmlNode =
   let parentNode = node.select(parent)
-  if parentNode.isNil(): return
+  if parentNode == nil: return
   result = parentNode.select(child)
 
-proc getAttr*(node: XmlNode; attr: string; default=""): string =
-  if node.isNil or node.attrs.isNil: return default
-  return node.attrs.getOrDefault(attr)
-
-proc selectAttr*(node: XmlNode; selector: string; attr: string; default=""): string =
+proc selectAttr*(node: XmlNode; selector: string; attr: string): string =
   let res = node.select(selector)
-  if res == nil: "" else: res.getAttr(attr, default)
+  if res == nil: "" else: res.attr(attr)
 
 proc selectText*(node: XmlNode; selector: string): string =
   let res = node.select(selector)
@@ -35,9 +33,9 @@ proc selectText*(node: XmlNode; selector: string): string =
 
 proc getHeader(profile: XmlNode): XmlNode =
   result = profile.select(".permalink-header")
-  if result.isNil:
+  if result == nil:
     result = profile.select(".stream-item-header")
-  if result.isNil:
+  if result == nil:
     result = profile.select(".ProfileCard-userFields")
 
 proc isVerified*(profile: XmlNode): bool =
@@ -54,7 +52,7 @@ proc getUsername*(profile: XmlNode; selector: string): string =
 
 proc emojify*(node: XmlNode) =
   for i in node.selectAll(".Emoji"):
-    i.add newText(i.getAttr("alt"))
+    i.add newText(i.attr("alt"))
 
 proc getQuoteText*(tweet: XmlNode): string =
   let text = tweet.select(".QuoteTweet-text")
@@ -71,7 +69,7 @@ proc getTweetText*(tweet: XmlNode): string =
   emojify(text)
   result = stripText(text.innerText())
 
-  if not quote.isNil and link.len > 0:
+  if quote != nil and link.len > 0:
     result = result.replace(link, "")
 
   result = stripTwitterUrls(result)
@@ -80,8 +78,8 @@ proc getTime(tweet: XmlNode): XmlNode =
   tweet.select(".js-short-timestamp")
 
 proc getTimestamp*(tweet: XmlNode): Time =
-  let time = getTime(tweet).getAttr("data-time", "0")
-  fromUnix(parseInt(time))
+  let time = getTime(tweet).attr("data-time")
+  fromUnix(if time.len > 0: parseInt(time) else: 0)
 
 proc getShortTime*(tweet: XmlNode): string =
   getTime(tweet).innerText()
@@ -105,8 +103,8 @@ proc getBanner*(tweet: XmlNode): string =
 
 proc getPopupStats*(profile: var Profile; node: XmlNode) =
   for s in node.selectAll( ".ProfileCardStats-statLink"):
-    let text = s.getAttr("title").split(" ")[0]
-    case s.getAttr("href").split("/")[^1]
+    let text = s.attr("title").split(" ")[0]
+    case s.attr("href").split("/")[^1]
     of "followers": profile.followers = text
     of "following": profile.following = text
     else: profile.tweets = text
@@ -115,7 +113,7 @@ proc getIntentStats*(profile: var Profile; node: XmlNode) =
   profile.tweets = "?"
   for s in node.selectAll( "dd.count > a"):
     let text = s.innerText()
-    case s.getAttr("href").split("/")[^1]
+    case s.attr("href").split("/")[^1]
     of "followers": profile.followers = text
     of "following": profile.following = text
 
@@ -132,7 +130,7 @@ proc getTweetStats*(tweet: Tweet; node: XmlNode) =
 
 proc getGif(player: XmlNode): Gif =
   let
-    thumb = player.getAttr("style").replace(thumbRegex, "$1")
+    thumb = player.attr("style").replace(thumbRegex, "$1")
     id = thumb.replace(gifRegex, "$1")
     url = fmt"https://video.twimg.com/tweet_video/{id}.mp4"
   Gif(url: url, thumb: thumb)
@@ -142,28 +140,28 @@ proc getTweetMedia*(tweet: Tweet; node: XmlNode) =
     tweet.photos.add photo.attrs["data-image-url"]
 
   let player = node.select(".PlayableMedia")
-  if player.isNil:
+  if player == nil:
     return
 
-  if "gif" in player.getAttr("class"):
+  if "gif" in player.attr("class"):
     tweet.gif = some(getGif(player.select(".PlayableMedia-player")))
-  elif "video" in player.getAttr("class"):
+  elif "video" in player.attr("class"):
     tweet.video = some(Video())
 
 proc getQuoteMedia*(quote: var Quote; node: XmlNode) =
   let sensitive = node.select(".QuoteTweet--sensitive")
-  if not sensitive.isNil:
+  if sensitive != nil:
     quote.sensitive = true
     return
 
   let media = node.select(".QuoteMedia")
-  if not media.isNil:
+  if media != nil:
     quote.thumb = some(media.selectAttr("img", "src"))
 
   let badge = node.select(".AdaptiveMedia-badgeText")
   let gifBadge = node.select(".Icon--gifBadge")
 
-  if not badge.isNil:
+  if badge != nil:
     quote.badge = some(badge.innerText())
-  elif not gifBadge.isNil:
+  elif gifBadge != nil:
     quote.badge = some("GIF")